pci-v5.12-changes

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCgAyFiEEgMe7l+5h9hnxdsnuWYigwDrT+vwFAmA2xiQUHGJoZWxnYWFz
 QGdvb2dsZS5jb20ACgkQWYigwDrT+vzRDA/9GCyEskI9DMtyT9UeoTMzpHcUZpaU
 eCbLa2BSPjOKlrHLnPY7IwE0nT7ihe4OOcm8uOYOWtulE46XJNCHfxlUYP3SbI0Y
 JlG0FBCh4ldzCzzKsftwkSvVhk+gn+ms9ucJ8q2iBSOXVhG/41IbX7++8IfbQM4v
 VHjdYUmTCCiOSRDtBVi82p4+GAHxH8IhaB0gDNb1Q7myj+qJKL5nKjK/nukgO0fO
 UpCnSxyua48Ij+c59Y1QAIhGeORq5Gg5Q4ussY3FxS9ovhZODEGQwCFniTfilqRw
 wEB9Fb8tiPY60ljEyDPnERMkiW69zutTJqOY4LfwmoRM9IEbxD6VPIqF5gin8sB7
 pHhX4KUU+eB1hQdK9SGKjkwyehquNKzTdxsu2jccltOKwBm5jcXYeOvu2bJTzZn+
 rrZPYJoA1dQig3bEuOzsBxvW4Jaj7IsVfVcao4OzXyh8Y7tLr9kVDXxr7JC/EkPM
 zRK24yglERD2J1JXgNMvOuJQj6JmRHhEbV/faZci8x8ZEaz1FawRAUZqHf/gGmnW
 2CllarHbRnchPyD8btv03Mp84WG6fCfKy7zG2D8HxOsiStDO/5ICehHtGcvYg7IL
 RuE4Tj8OKdcbw/8cO4C3842FqiSj34+jooNIHSLyBqcpJam6VsN4XqNIZCL+DeG5
 Q2JXruAaahTWOZg=
 =GXL5
 -----END PGP SIGNATURE-----

Merge tag 'pci-v5.12-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci

Pull PCI updates from Bjorn Helgaas:
 "Enumeration:
   - Remove unnecessary locking around _OSC (Bjorn Helgaas)
   - Clarify message about _OSC failure (Bjorn Helgaas)
   - Remove notification of PCIe bandwidth changes (Bjorn Helgaas)
   - Tidy checking of syscall user config accessors (Heiner Kallweit)

  Resource management:
   - Decline to resize resources if boot config must be preserved (Ard
     Biesheuvel)
   - Fix pci_register_io_range() memory leak (Geert Uytterhoeven)

  Error handling (Keith Busch):
   - Clear error status from the correct device
   - Retain error recovery status so drivers can use it after reset
   - Log the type of Port (Root or Switch Downstream) that we reset
   - Always request a reset for Downstream Ports in frozen state

  Endpoint framework and NTB (Kishon Vijay Abraham I):
   - Make *_get_first_free_bar() take into account 64 bit BAR
   - Add helper API to get the 'next' unreserved BAR
   - Make *_free_bar() return error codes on failure
   - Remove unused pci_epf_match_device()
   - Add support to associate secondary EPC with EPF
   - Add support in configfs to associate two EPCs with EPF
   - Add pci_epc_ops to map MSI IRQ
   - Add pci_epf_ops to expose function-specific attrs
   - Allow user to create sub-directory of 'EPF Device' directory
   - Implement ->msi_map_irq() ops for cadence
   - Configure LM_EP_FUNC_CFG based on epc->function_num_map for cadence
   - Add EP function driver to provide NTB functionality
   - Add support for EPF PCI Non-Transparent Bridge
   - Add specification for PCI NTB function device
   - Add PCI endpoint NTB function user guide
   - Add configfs binding documentation for pci-ntb endpoint function

  Broadcom STB PCIe controller driver:
   - Add support for BCM4908 and external PERST# signal controller
     (Rafał Miłecki)

  Cadence PCIe controller driver:
   - Retrain Link to work around Gen2 training defect (Nadeem Athani)
   - Fix merge botch in cdns_pcie_host_map_dma_ranges() (Krzysztof
     Wilczyński)

  Freescale Layerscape PCIe controller driver:
   - Add LX2160A rev2 EP mode support (Hou Zhiqiang)
   - Convert to builtin_platform_driver() (Michael Walle)

  MediaTek PCIe controller driver:
   - Fix OF node reference leak (Krzysztof Wilczyński)

  Microchip PolarFlare PCIe controller driver:
   - Add Microchip PolarFire PCIe controller driver (Daire McNamara)

  Qualcomm PCIe controller driver:
   - Use PHY_REFCLK_USE_PAD only for ipq8064 (Ansuel Smith)
   - Add support for ddrss_sf_tbu clock for sm8250 (Dmitry Baryshkov)

  Renesas R-Car PCIe controller driver:
   - Drop PCIE_RCAR config option (Lad Prabhakar)
   - Always allocate MSI addresses in 32bit space (Marek Vasut)

  Rockchip PCIe controller driver:
   - Add FriendlyARM NanoPi M4B DT binding (Chen-Yu Tsai)
   - Make 'ep-gpios' DT property optional (Chen-Yu Tsai)

  Synopsys DesignWare PCIe controller driver:
   - Work around ECRC configuration hardware defect (Vidya Sagar)
   - Drop support for config space in DT 'ranges' (Rob Herring)
   - Change size to u64 for EP outbound iATU (Shradha Todi)
   - Add upper limit address for outbound iATU (Shradha Todi)
   - Make dw_pcie ops optional (Jisheng Zhang)
   - Remove unnecessary dw_pcie_ops from al driver (Jisheng Zhang)

  Xilinx Versal CPM PCIe controller driver:
   - Fix OF node reference leak (Pan Bian)

  Miscellaneous:
   - Remove tango host controller driver (Arnd Bergmann)
   - Remove IRQ handler & data together (altera-msi, brcmstb, dwc)
     (Martin Kaiser)
   - Fix xgene-msi race in installing chained IRQ handler (Martin
     Kaiser)
   - Apply CONFIG_PCI_DEBUG to entire drivers/pci hierarchy (Junhao He)
   - Fix pci-bridge-emul array overruns (Russell King)
   - Remove obsolete uses of WARN_ON(in_interrupt()) (Sebastian Andrzej
     Siewior)"

* tag 'pci-v5.12-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (69 commits)
  PCI: qcom: Use PHY_REFCLK_USE_PAD only for ipq8064
  PCI: qcom: Add support for ddrss_sf_tbu clock
  dt-bindings: PCI: qcom: Document ddrss_sf_tbu clock for sm8250
  PCI: al: Remove useless dw_pcie_ops
  PCI: dwc: Don't assume the ops in dw_pcie always exist
  PCI: dwc: Add upper limit address for outbound iATU
  PCI: dwc: Change size to u64 for EP outbound iATU
  PCI: dwc: Drop support for config space in 'ranges'
  PCI: layerscape: Convert to builtin_platform_driver()
  PCI: layerscape: Add LX2160A rev2 EP mode support
  dt-bindings: PCI: layerscape: Add LX2160A rev2 compatible strings
  PCI: dwc: Work around ECRC configuration issue
  PCI/portdrv: Report reset for frozen channel
  PCI/AER: Specify the type of Port that was reset
  PCI/ERR: Retain status from error notification
  PCI/AER: Clear AER status from Root Port when resetting Downstream Port
  PCI/ERR: Clear status of the reporting device
  dt-bindings: arm: rockchip: Add FriendlyARM NanoPi M4B
  PCI: rockchip: Make 'ep-gpios' DT property optional
  Documentation: PCI: Add PCI endpoint NTB function user guide
  ...
This commit is contained in:
Linus Torvalds 2021-02-25 09:56:08 -08:00
commit 5b47b10e8f
73 changed files with 5543 additions and 781 deletions

View File

@ -0,0 +1,38 @@
.. SPDX-License-Identifier: GPL-2.0
==========================
PCI NTB Endpoint Function
==========================
1) Create a subdirectory to pci_epf_ntb directory in configfs.
Standard EPF Configurable Fields:
================ ===========================================================
vendorid should be 0x104c
deviceid should be 0xb00d for TI's J721E SoC
revid don't care
progif_code don't care
subclass_code should be 0x00
baseclass_code should be 0x5
cache_line_size don't care
subsys_vendor_id don't care
subsys_id don't care
interrupt_pin don't care
msi_interrupts don't care
msix_interrupts don't care
================ ===========================================================
2) Create a subdirectory to directory created in 1
NTB EPF specific configurable fields:
================ ===========================================================
db_count Number of doorbells; default = 4
mw1 size of memory window1
mw2 size of memory window2
mw3 size of memory window3
mw4 size of memory window4
num_mws Number of memory windows; max = 4
spad_count Number of scratchpad registers; default = 64
================ ===========================================================

View File

@ -11,5 +11,8 @@ PCI Endpoint Framework
pci-endpoint-cfs
pci-test-function
pci-test-howto
pci-ntb-function
pci-ntb-howto
function/binding/pci-test
function/binding/pci-ntb

View File

@ -68,6 +68,16 @@ created)
... subsys_vendor_id
... subsys_id
... interrupt_pin
... primary/
... <Symlink EPC Device1>/
... secondary/
... <Symlink EPC Device2>/
If an EPF device has to be associated with 2 EPCs (like in the case of
Non-transparent bridge), symlink of endpoint controller connected to primary
interface should be added in 'primary' directory and symlink of endpoint
controller connected to secondary interface should be added in 'secondary'
directory.
EPC Device
==========

View File

@ -0,0 +1,348 @@
.. SPDX-License-Identifier: GPL-2.0
=================
PCI NTB Function
=================
:Author: Kishon Vijay Abraham I <kishon@ti.com>
PCI Non-Transparent Bridges (NTB) allow two host systems to communicate
with each other by exposing each host as a device to the other host.
NTBs typically support the ability to generate interrupts on the remote
machine, expose memory ranges as BARs, and perform DMA. They also support
scratchpads, which are areas of memory within the NTB that are accessible
from both machines.
PCI NTB Function allows two different systems (or hosts) to communicate
with each other by configuring the endpoint instances in such a way that
transactions from one system are routed to the other system.
In the below diagram, PCI NTB function configures the SoC with multiple
PCI Endpoint (EP) instances in such a way that transactions from one EP
controller are routed to the other EP controller. Once PCI NTB function
configures the SoC with multiple EP instances, HOST1 and HOST2 can
communicate with each other using SoC as a bridge.
.. code-block:: text
+-------------+ +-------------+
| | | |
| HOST1 | | HOST2 |
| | | |
+------^------+ +------^------+
| |
| |
+---------|-------------------------------------------------|---------+
| +------v------+ +------v------+ |
| | | | | |
| | EP | | EP | |
| | CONTROLLER1 | | CONTROLLER2 | |
| | <-----------------------------------> | |
| | | | | |
| | | | | |
| | | SoC With Multiple EP Instances | | |
| | | (Configured using NTB Function) | | |
| +-------------+ +-------------+ |
+---------------------------------------------------------------------+
Constructs used for Implementing NTB
====================================
1) Config Region
2) Self Scratchpad Registers
3) Peer Scratchpad Registers
4) Doorbell (DB) Registers
5) Memory Window (MW)
Config Region:
--------------
Config Region is a construct that is specific to NTB implemented using NTB
Endpoint Function Driver. The host and endpoint side NTB function driver will
exchange information with each other using this region. Config Region has
Control/Status Registers for configuring the Endpoint Controller. Host can
write into this region for configuring the outbound Address Translation Unit
(ATU) and to indicate the link status. Endpoint can indicate the status of
commands issued by host in this region. Endpoint can also indicate the
scratchpad offset and number of memory windows to the host using this region.
The format of Config Region is given below. All the fields here are 32 bits.
.. code-block:: text
+------------------------+
| COMMAND |
+------------------------+
| ARGUMENT |
+------------------------+
| STATUS |
+------------------------+
| TOPOLOGY |
+------------------------+
| ADDRESS (LOWER 32) |
+------------------------+
| ADDRESS (UPPER 32) |
+------------------------+
| SIZE |
+------------------------+
| NO OF MEMORY WINDOW |
+------------------------+
| MEMORY WINDOW1 OFFSET |
+------------------------+
| SPAD OFFSET |
+------------------------+
| SPAD COUNT |
+------------------------+
| DB ENTRY SIZE |
+------------------------+
| DB DATA |
+------------------------+
| : |
+------------------------+
| : |
+------------------------+
| DB DATA |
+------------------------+
COMMAND:
NTB function supports three commands:
CMD_CONFIGURE_DOORBELL (0x1): Command to configure doorbell. Before
invoking this command, the host should allocate and initialize
MSI/MSI-X vectors (i.e., initialize the MSI/MSI-X Capability in the
Endpoint). The endpoint on receiving this command will configure
the outbound ATU such that transactions to Doorbell BAR will be routed
to the MSI/MSI-X address programmed by the host. The ARGUMENT
register should be populated with number of DBs to configure (in the
lower 16 bits) and if MSI or MSI-X should be configured (BIT 16).
CMD_CONFIGURE_MW (0x2): Command to configure memory window (MW). The
host invokes this command after allocating a buffer that can be
accessed by remote host. The allocated address should be programmed
in the ADDRESS register (64 bit), the size should be programmed in
the SIZE register and the memory window index should be programmed
in the ARGUMENT register. The endpoint on receiving this command
will configure the outbound ATU such that transactions to MW BAR
are routed to the address provided by the host.
CMD_LINK_UP (0x3): Command to indicate an NTB application is
bound to the EP device on the host side. Once the endpoint
receives this command from both the hosts, the endpoint will
raise a LINK_UP event to both the hosts to indicate the host
NTB applications can start communicating with each other.
ARGUMENT:
The value of this register is based on the commands issued in
command register. See COMMAND section for more information.
TOPOLOGY:
Set to NTB_TOPO_B2B_USD for Primary interface
Set to NTB_TOPO_B2B_DSD for Secondary interface
ADDRESS/SIZE:
Address and Size to be used while configuring the memory window.
See "CMD_CONFIGURE_MW" for more info.
MEMORY WINDOW1 OFFSET:
Memory Window 1 and Doorbell registers are packed together in the
same BAR. The initial portion of the region will have doorbell
registers and the latter portion of the region is for memory window 1.
This register will specify the offset of the memory window 1.
NO OF MEMORY WINDOW:
Specifies the number of memory windows supported by the NTB device.
SPAD OFFSET:
Self scratchpad region and config region are packed together in the
same BAR. The initial portion of the region will have config region
and the latter portion of the region is for self scratchpad. This
register will specify the offset of the self scratchpad registers.
SPAD COUNT:
Specifies the number of scratchpad registers supported by the NTB
device.
DB ENTRY SIZE:
Used to determine the offset within the DB BAR that should be written
in order to raise doorbell. EPF NTB can use either MSI or MSI-X to
ring doorbell (MSI-X support will be added later). MSI uses same
address for all the interrupts and MSI-X can provide different
addresses for different interrupts. The MSI/MSI-X address is provided
by the host and the address it gives is based on the MSI/MSI-X
implementation supported by the host. For instance, ARM platform
using GIC ITS will have the same MSI-X address for all the interrupts.
In order to support all the combinations and use the same mechanism
for both MSI and MSI-X, EPF NTB allocates a separate region in the
Outbound Address Space for each of the interrupts. This region will
be mapped to the MSI/MSI-X address provided by the host. If a host
provides the same address for all the interrupts, all the regions
will be translated to the same address. If a host provides different
addresses, the regions will be translated to different addresses. This
will ensure there is no difference while raising the doorbell.
DB DATA:
EPF NTB supports 32 interrupts, so there are 32 DB DATA registers.
This holds the MSI/MSI-X data that has to be written to MSI address
for raising doorbell interrupt. This will be populated by EPF NTB
while invoking CMD_CONFIGURE_DOORBELL.
Scratchpad Registers:
---------------------
Each host has its own register space allocated in the memory of NTB endpoint
controller. They are both readable and writable from both sides of the bridge.
They are used by applications built over NTB and can be used to pass control
and status information between both sides of a device.
Scratchpad registers has 2 parts
1) Self Scratchpad: Host's own register space
2) Peer Scratchpad: Remote host's register space.
Doorbell Registers:
-------------------
Doorbell Registers are used by the hosts to interrupt each other.
Memory Window:
--------------
Actual transfer of data between the two hosts will happen using the
memory window.
Modeling Constructs:
====================
There are 5 or more distinct regions (config, self scratchpad, peer
scratchpad, doorbell, one or more memory windows) to be modeled to achieve
NTB functionality. At least one memory window is required while more than
one is permitted. All these regions should be mapped to BARs for hosts to
access these regions.
If one 32-bit BAR is allocated for each of these regions, the scheme would
look like this:
====== ===============
BAR NO CONSTRUCTS USED
====== ===============
BAR0 Config Region
BAR1 Self Scratchpad
BAR2 Peer Scratchpad
BAR3 Doorbell
BAR4 Memory Window 1
BAR5 Memory Window 2
====== ===============
However if we allocate a separate BAR for each of the regions, there would not
be enough BARs for all the regions in a platform that supports only 64-bit
BARs.
In order to be supported by most of the platforms, the regions should be
packed and mapped to BARs in a way that provides NTB functionality and
also makes sure the host doesn't access any region that it is not supposed
to.
The following scheme is used in EPF NTB Function:
====== ===============================
BAR NO CONSTRUCTS USED
====== ===============================
BAR0 Config Region + Self Scratchpad
BAR1 Peer Scratchpad
BAR2 Doorbell + Memory Window 1
BAR3 Memory Window 2
BAR4 Memory Window 3
BAR5 Memory Window 4
====== ===============================
With this scheme, for the basic NTB functionality 3 BARs should be sufficient.
Modeling Config/Scratchpad Region:
----------------------------------
.. code-block:: text
+-----------------+------->+------------------+ +-----------------+
| BAR0 | | CONFIG REGION | | BAR0 |
+-----------------+----+ +------------------+<-------+-----------------+
| BAR1 | | |SCRATCHPAD REGION | | BAR1 |
+-----------------+ +-->+------------------+<-------+-----------------+
| BAR2 | Local Memory | BAR2 |
+-----------------+ +-----------------+
| BAR3 | | BAR3 |
+-----------------+ +-----------------+
| BAR4 | | BAR4 |
+-----------------+ +-----------------+
| BAR5 | | BAR5 |
+-----------------+ +-----------------+
EP CONTROLLER 1 EP CONTROLLER 2
Above diagram shows Config region + Scratchpad region for HOST1 (connected to
EP controller 1) allocated in local memory. The HOST1 can access the config
region and scratchpad region (self scratchpad) using BAR0 of EP controller 1.
The peer host (HOST2 connected to EP controller 2) can also access this
scratchpad region (peer scratchpad) using BAR1 of EP controller 2. This
diagram shows the case where Config region and Scratchpad regions are allocated
for HOST1, however the same is applicable for HOST2.
Modeling Doorbell/Memory Window 1:
----------------------------------
.. code-block:: text
+-----------------+ +----->+----------------+-----------+-----------------+
| BAR0 | | | Doorbell 1 +-----------> MSI-X ADDRESS 1 |
+-----------------+ | +----------------+ +-----------------+
| BAR1 | | | Doorbell 2 +---------+ | |
+-----------------+----+ +----------------+ | | |
| BAR2 | | Doorbell 3 +-------+ | +-----------------+
+-----------------+----+ +----------------+ | +-> MSI-X ADDRESS 2 |
| BAR3 | | | Doorbell 4 +-----+ | +-----------------+
+-----------------+ | |----------------+ | | | |
| BAR4 | | | | | | +-----------------+
+-----------------+ | | MW1 +---+ | +-->+ MSI-X ADDRESS 3||
| BAR5 | | | | | | +-----------------+
+-----------------+ +----->-----------------+ | | | |
EP CONTROLLER 1 | | | | +-----------------+
| | | +---->+ MSI-X ADDRESS 4 |
+----------------+ | +-----------------+
EP CONTROLLER 2 | | |
(OB SPACE) | | |
+-------> MW1 |
| |
| |
+-----------------+
| |
| |
| |
| |
| |
+-----------------+
PCI Address Space
(Managed by HOST2)
Above diagram shows how the doorbell and memory window 1 is mapped so that
HOST1 can raise doorbell interrupt on HOST2 and also how HOST1 can access
buffers exposed by HOST2 using memory window1 (MW1). Here doorbell and
memory window 1 regions are allocated in EP controller 2 outbound (OB) address
space. Allocating and configuring BARs for doorbell and memory window1
is done during the initialization phase of NTB endpoint function driver.
Mapping from EP controller 2 OB space to PCI address space is done when HOST2
sends CMD_CONFIGURE_MW/CMD_CONFIGURE_DOORBELL.
Modeling Optional Memory Windows:
---------------------------------
This is modeled the same was as MW1 but each of the additional memory windows
is mapped to separate BARs.

View File

@ -0,0 +1,161 @@
.. SPDX-License-Identifier: GPL-2.0
===================================================================
PCI Non-Transparent Bridge (NTB) Endpoint Function (EPF) User Guide
===================================================================
:Author: Kishon Vijay Abraham I <kishon@ti.com>
This document is a guide to help users use pci-epf-ntb function driver
and ntb_hw_epf host driver for NTB functionality. The list of steps to
be followed in the host side and EP side is given below. For the hardware
configuration and internals of NTB using configurable endpoints see
Documentation/PCI/endpoint/pci-ntb-function.rst
Endpoint Device
===============
Endpoint Controller Devices
---------------------------
For implementing NTB functionality at least two endpoint controller devices
are required.
To find the list of endpoint controller devices in the system::
# ls /sys/class/pci_epc/
2900000.pcie-ep 2910000.pcie-ep
If PCI_ENDPOINT_CONFIGFS is enabled::
# ls /sys/kernel/config/pci_ep/controllers
2900000.pcie-ep 2910000.pcie-ep
Endpoint Function Drivers
-------------------------
To find the list of endpoint function drivers in the system::
# ls /sys/bus/pci-epf/drivers
pci_epf_ntb pci_epf_ntb
If PCI_ENDPOINT_CONFIGFS is enabled::
# ls /sys/kernel/config/pci_ep/functions
pci_epf_ntb pci_epf_ntb
Creating pci-epf-ntb Device
----------------------------
PCI endpoint function device can be created using the configfs. To create
pci-epf-ntb device, the following commands can be used::
# mount -t configfs none /sys/kernel/config
# cd /sys/kernel/config/pci_ep/
# mkdir functions/pci_epf_ntb/func1
The "mkdir func1" above creates the pci-epf-ntb function device that will
be probed by pci_epf_ntb driver.
The PCI endpoint framework populates the directory with the following
configurable fields::
# ls functions/pci_epf_ntb/func1
baseclass_code deviceid msi_interrupts pci-epf-ntb.0
progif_code secondary subsys_id vendorid
cache_line_size interrupt_pin msix_interrupts primary
revid subclass_code subsys_vendor_id
The PCI endpoint function driver populates these entries with default values
when the device is bound to the driver. The pci-epf-ntb driver populates
vendorid with 0xffff and interrupt_pin with 0x0001::
# cat functions/pci_epf_ntb/func1/vendorid
0xffff
# cat functions/pci_epf_ntb/func1/interrupt_pin
0x0001
Configuring pci-epf-ntb Device
-------------------------------
The user can configure the pci-epf-ntb device using its configfs entry. In order
to change the vendorid and the deviceid, the following
commands can be used::
# echo 0x104c > functions/pci_epf_ntb/func1/vendorid
# echo 0xb00d > functions/pci_epf_ntb/func1/deviceid
In order to configure NTB specific attributes, a new sub-directory to func1
should be created::
# mkdir functions/pci_epf_ntb/func1/pci_epf_ntb.0/
The NTB function driver will populate this directory with various attributes
that can be configured by the user::
# ls functions/pci_epf_ntb/func1/pci_epf_ntb.0/
db_count mw1 mw2 mw3 mw4 num_mws
spad_count
A sample configuration for NTB function is given below::
# echo 4 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/db_count
# echo 128 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/spad_count
# echo 2 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/num_mws
# echo 0x100000 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/mw1
# echo 0x100000 > functions/pci_epf_ntb/func1/pci_epf_ntb.0/mw2
Binding pci-epf-ntb Device to EP Controller
--------------------------------------------
NTB function device should be attached to two PCI endpoint controllers
connected to the two hosts. Use the 'primary' and 'secondary' entries
inside NTB function device to attach one PCI endpoint controller to
primary interface and the other PCI endpoint controller to the secondary
interface::
# ln -s controllers/2900000.pcie-ep/ functions/pci-epf-ntb/func1/primary
# ln -s controllers/2910000.pcie-ep/ functions/pci-epf-ntb/func1/secondary
Once the above step is completed, both the PCI endpoint controllers are ready to
establish a link with the host.
Start the Link
--------------
In order for the endpoint device to establish a link with the host, the _start_
field should be populated with '1'. For NTB, both the PCI endpoint controllers
should establish link with the host::
# echo 1 > controllers/2900000.pcie-ep/start
# echo 1 > controllers/2910000.pcie-ep/start
RootComplex Device
==================
lspci Output
------------
Note that the devices listed here correspond to the values populated in
"Creating pci-epf-ntb Device" section above::
# lspci
0000:00:00.0 PCI bridge: Texas Instruments Device b00d
0000:01:00.0 RAM memory: Texas Instruments Device b00d
Using ntb_hw_epf Device
-----------------------
The host side software follows the standard NTB software architecture in Linux.
All the existing client side NTB utilities like NTB Transport Client and NTB
Netdev, NTB Ping Pong Test Client and NTB Tool Test Client can be used with NTB
function device.
For more information on NTB see
:doc:`Non-Transparent Bridge <../../driver-api/ntb>`

View File

@ -132,6 +132,7 @@ properties:
- enum:
- friendlyarm,nanopc-t4
- friendlyarm,nanopi-m4
- friendlyarm,nanopi-m4b
- friendlyarm,nanopi-neo4
- const: rockchip,rk3399

View File

@ -14,6 +14,7 @@ properties:
items:
- enum:
- brcm,bcm2711-pcie # The Raspberry Pi 4
- brcm,bcm4908-pcie
- brcm,bcm7211-pcie # Broadcom STB version of RPi4
- brcm,bcm7278-pcie # Broadcom 7278 Arm
- brcm,bcm7216-pcie # Broadcom 7216 Arm
@ -63,15 +64,6 @@ properties:
aspm-no-l0s: true
resets:
description: for "brcm,bcm7216-pcie", must be a valid reset
phandle pointing to the RESCAL reset controller provider node.
$ref: "/schemas/types.yaml#/definitions/phandle"
reset-names:
items:
- const: rescal
brcm,scb-sizes:
description: u64 giving the 64bit PCIe memory
viewport size of a memory controller. There may be up to
@ -98,12 +90,39 @@ required:
allOf:
- $ref: /schemas/pci/pci-bus.yaml#
- if:
properties:
compatible:
contains:
const: brcm,bcm4908-pcie
then:
properties:
resets:
items:
- description: reset controller handling the PERST# signal
reset-names:
items:
- const: perst
required:
- resets
- reset-names
- if:
properties:
compatible:
contains:
const: brcm,bcm7216-pcie
then:
properties:
resets:
items:
- description: phandle pointing to the RESCAL reset controller
reset-names:
items:
- const: rescal
required:
- resets
- reset-names

View File

@ -26,6 +26,7 @@ Required properties:
"fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep"
"fsl,ls1088a-pcie-ep", "fsl,ls-pcie-ep"
"fsl,ls2088a-pcie-ep", "fsl,ls-pcie-ep"
"fsl,lx2160ar2-pcie-ep", "fsl,ls-pcie-ep"
- reg: base addresses and lengths of the PCIe controller register blocks.
- interrupts: A list of interrupt outputs of the controller. Must contain an
entry for each entry in the interrupt-names property.

View File

@ -0,0 +1,92 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/microchip,pcie-host.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip PCIe Root Port Bridge Controller Device Tree Bindings
maintainers:
- Daire McNamara <daire.mcnamara@microchip.com>
allOf:
- $ref: /schemas/pci/pci-bus.yaml#
properties:
compatible:
const: microchip,pcie-host-1.0 # PolarFire
reg:
maxItems: 2
reg-names:
items:
- const: cfg
- const: apb
interrupts:
minItems: 1
maxItems: 2
items:
- description: PCIe host controller
- description: builtin MSI controller
interrupt-names:
minItems: 1
maxItems: 2
items:
- const: pcie
- const: msi
ranges:
maxItems: 1
msi-controller:
description: Identifies the node as an MSI controller.
msi-parent:
description: MSI controller the device is capable of using.
required:
- reg
- reg-names
- "#interrupt-cells"
- interrupts
- interrupt-map-mask
- interrupt-map
- msi-controller
unevaluatedProperties: false
examples:
- |
soc {
#address-cells = <2>;
#size-cells = <2>;
pcie0: pcie@2030000000 {
compatible = "microchip,pcie-host-1.0";
reg = <0x0 0x70000000 0x0 0x08000000>,
<0x0 0x43000000 0x0 0x00010000>;
reg-names = "cfg", "apb";
device_type = "pci";
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
interrupts = <119>;
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0 0 0 1 &pcie_intc0 0>,
<0 0 0 2 &pcie_intc0 1>,
<0 0 0 3 &pcie_intc0 2>,
<0 0 0 4 &pcie_intc0 3>;
interrupt-parent = <&plic0>;
msi-parent = <&pcie0>;
msi-controller;
bus-range = <0x00 0x7f>;
ranges = <0x03000000 0x0 0x78000000 0x0 0x78000000 0x0 0x04000000>;
pcie_intc0: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
};
};
};

View File

@ -132,8 +132,8 @@
- "master_bus" AXI Master clock
- "slave_bus" AXI Slave clock
-clock-names:
Usage: required for sdm845 and sm8250
- clock-names:
Usage: required for sdm845
Value type: <stringlist>
Definition: Should contain the following entries
- "aux" Auxiliary clock
@ -144,6 +144,19 @@
- "tbu" PCIe TBU clock
- "pipe" PIPE clock
- clock-names:
Usage: required for sm8250
Value type: <stringlist>
Definition: Should contain the following entries
- "aux" Auxiliary clock
- "cfg" Configuration clock
- "bus_master" Master AXI clock
- "bus_slave" Slave AXI clock
- "slave_q2a" Slave Q2A clock
- "tbu" PCIe TBU clock
- "ddrss_sf_tbu" PCIe SF TBU clock
- "pipe" PIPE clock
- resets:
Usage: required
Value type: <prop-encoded-array>

View File

@ -2578,7 +2578,7 @@ L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/clk/keystone/
ARM/TEXAS INSTRUMENT KEYSTONE ClOCKSOURCE
ARM/TEXAS INSTRUMENT KEYSTONE CLOCKSOURCE
M: Santosh Shilimkar <ssantosh@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-kernel@vger.kernel.org
@ -13890,6 +13890,13 @@ S: Supported
F: Documentation/devicetree/bindings/pci/mediatek*
F: drivers/pci/controller/*mediatek*
PCIE DRIVER FOR MICROCHIP
M: Daire McNamara <daire.mcnamara@microchip.com>
L: linux-pci@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/pci/microchip*
F: drivers/pci/controller/*microchip*
PCIE DRIVER FOR QUALCOMM MSM
M: Stanimir Varbanov <svarbanov@mm-sol.com>
L: linux-pci@vger.kernel.org

View File

@ -44,7 +44,7 @@ static inline int __test_facility(unsigned long nr, void *facilities)
}
/*
* The test_facility function uses the bit odering where the MSB is bit 0.
* The test_facility function uses the bit ordering where the MSB is bit 0.
* That makes it easier to query facility bits with the bit number as
* documented in the Principles of Operation.
*/

View File

@ -56,8 +56,6 @@ static struct acpi_scan_handler pci_root_handler = {
},
};
static DEFINE_MUTEX(osc_lock);
/**
* acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge
* @handle: the ACPI CA node in question.
@ -223,12 +221,7 @@ static acpi_status acpi_pci_query_osc(struct acpi_pci_root *root,
static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
{
acpi_status status;
mutex_lock(&osc_lock);
status = acpi_pci_query_osc(root, flags, NULL);
mutex_unlock(&osc_lock);
return status;
return acpi_pci_query_osc(root, flags, NULL);
}
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
@ -353,10 +346,10 @@ EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
* _OSC bits the BIOS has granted control of, but its contents are meaningless
* on failure.
**/
acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
static acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
{
struct acpi_pci_root *root;
acpi_status status = AE_OK;
acpi_status status;
u32 ctrl, capbuf[3];
if (!mask)
@ -370,18 +363,16 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
if (!root)
return AE_NOT_EXIST;
mutex_lock(&osc_lock);
*mask = ctrl | root->osc_control_set;
/* No need to evaluate _OSC if the control was already granted. */
if ((root->osc_control_set & ctrl) == ctrl)
goto out;
return AE_OK;
/* Need to check the available controls bits before requesting them. */
while (*mask) {
status = acpi_pci_query_osc(root, root->osc_support_set, mask);
if (ACPI_FAILURE(status))
goto out;
return status;
if (ctrl == *mask)
break;
decode_osc_control(root, "platform does not support",
@ -392,21 +383,19 @@ acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 *mask, u32 req)
if ((ctrl & req) != req) {
decode_osc_control(root, "not requesting control; platform does not support",
req & ~(ctrl));
status = AE_SUPPORT;
goto out;
return AE_SUPPORT;
}
capbuf[OSC_QUERY_DWORD] = 0;
capbuf[OSC_SUPPORT_DWORD] = root->osc_support_set;
capbuf[OSC_CONTROL_DWORD] = ctrl;
status = acpi_pci_run_osc(handle, capbuf, mask);
if (ACPI_SUCCESS(status))
root->osc_control_set = *mask;
out:
mutex_unlock(&osc_lock);
return status;
if (ACPI_FAILURE(status))
return status;
root->osc_control_set = *mask;
return AE_OK;
}
EXPORT_SYMBOL(acpi_pci_osc_control_set);
static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm,
bool is_pcie)
@ -452,9 +441,8 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm,
if ((status == AE_NOT_FOUND) && !is_pcie)
return;
dev_info(&device->dev, "_OSC failed (%s)%s\n",
acpi_format_exception(status),
pcie_aspm_support_enabled() ? "; disabling ASPM" : "");
dev_info(&device->dev, "_OSC: platform retains control of PCIe features (%s)\n",
acpi_format_exception(status));
return;
}
@ -510,7 +498,7 @@ static void negotiate_os_control(struct acpi_pci_root *root, int *no_aspm,
} else {
decode_osc_control(root, "OS requested", requested);
decode_osc_control(root, "platform willing to grant", control);
dev_info(&device->dev, "_OSC failed (%s); disabling ASPM\n",
dev_info(&device->dev, "_OSC: platform retains control of PCIe features (%s)\n",
acpi_format_exception(status));
/*

View File

@ -141,7 +141,7 @@ static void qxl_drm_release(struct drm_device *dev)
/*
* TODO: qxl_device_fini() call should be in qxl_pci_remove(),
* reodering qxl_modeset_fini() + qxl_device_fini() calls is
* reordering qxl_modeset_fini() + qxl_device_fini() calls is
* non-trivial though.
*/
qxl_modeset_fini(qdev);

View File

@ -68,7 +68,6 @@
#define PCI_ENDPOINT_TEST_FLAGS 0x2c
#define FLAG_USE_DMA BIT(0)
#define PCI_DEVICE_ID_TI_J721E 0xb00d
#define PCI_DEVICE_ID_TI_AM654 0xb00c
#define PCI_DEVICE_ID_LS1088A 0x80c0

View File

@ -870,7 +870,7 @@ struct iwl_fw_dbg_trigger_time_event {
* tx_bar: tid bitmap to configure on what tid the trigger should occur
* when a BAR is send (for an Rx BlocAck session).
* frame_timeout: tid bitmap to configure on what tid the trigger should occur
* when a frame times out in the reodering buffer.
* when a frame times out in the reordering buffer.
*/
struct iwl_fw_dbg_trigger_ba {
__le16 rx_ba_start;

View File

@ -2,4 +2,5 @@
source "drivers/ntb/hw/amd/Kconfig"
source "drivers/ntb/hw/idt/Kconfig"
source "drivers/ntb/hw/intel/Kconfig"
source "drivers/ntb/hw/epf/Kconfig"
source "drivers/ntb/hw/mscc/Kconfig"

View File

@ -2,4 +2,5 @@
obj-$(CONFIG_NTB_AMD) += amd/
obj-$(CONFIG_NTB_IDT) += idt/
obj-$(CONFIG_NTB_INTEL) += intel/
obj-$(CONFIG_NTB_EPF) += epf/
obj-$(CONFIG_NTB_SWITCHTEC) += mscc/

View File

@ -0,0 +1,6 @@
config NTB_EPF
tristate "Generic EPF Non-Transparent Bridge support"
depends on m
help
This driver supports EPF NTB on configurable endpoint.
If unsure, say N.

View File

@ -0,0 +1 @@
obj-$(CONFIG_NTB_EPF) += ntb_hw_epf.o

View File

@ -0,0 +1,753 @@
// SPDX-License-Identifier: GPL-2.0
/**
* Host side endpoint driver to implement Non-Transparent Bridge functionality
*
* Copyright (C) 2020 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/ntb.h>
#define NTB_EPF_COMMAND 0x0
#define CMD_CONFIGURE_DOORBELL 1
#define CMD_TEARDOWN_DOORBELL 2
#define CMD_CONFIGURE_MW 3
#define CMD_TEARDOWN_MW 4
#define CMD_LINK_UP 5
#define CMD_LINK_DOWN 6
#define NTB_EPF_ARGUMENT 0x4
#define MSIX_ENABLE BIT(16)
#define NTB_EPF_CMD_STATUS 0x8
#define COMMAND_STATUS_OK 1
#define COMMAND_STATUS_ERROR 2
#define NTB_EPF_LINK_STATUS 0x0A
#define LINK_STATUS_UP BIT(0)
#define NTB_EPF_TOPOLOGY 0x0C
#define NTB_EPF_LOWER_ADDR 0x10
#define NTB_EPF_UPPER_ADDR 0x14
#define NTB_EPF_LOWER_SIZE 0x18
#define NTB_EPF_UPPER_SIZE 0x1C
#define NTB_EPF_MW_COUNT 0x20
#define NTB_EPF_MW1_OFFSET 0x24
#define NTB_EPF_SPAD_OFFSET 0x28
#define NTB_EPF_SPAD_COUNT 0x2C
#define NTB_EPF_DB_ENTRY_SIZE 0x30
#define NTB_EPF_DB_DATA(n) (0x34 + (n) * 4)
#define NTB_EPF_DB_OFFSET(n) (0xB4 + (n) * 4)
#define NTB_EPF_MIN_DB_COUNT 3
#define NTB_EPF_MAX_DB_COUNT 31
#define NTB_EPF_MW_OFFSET 2
#define NTB_EPF_COMMAND_TIMEOUT 1000 /* 1 Sec */
enum pci_barno {
BAR_0,
BAR_1,
BAR_2,
BAR_3,
BAR_4,
BAR_5,
};
struct ntb_epf_dev {
struct ntb_dev ntb;
struct device *dev;
/* Mutex to protect providing commands to NTB EPF */
struct mutex cmd_lock;
enum pci_barno ctrl_reg_bar;
enum pci_barno peer_spad_reg_bar;
enum pci_barno db_reg_bar;
unsigned int mw_count;
unsigned int spad_count;
unsigned int db_count;
void __iomem *ctrl_reg;
void __iomem *db_reg;
void __iomem *peer_spad_reg;
unsigned int self_spad;
unsigned int peer_spad;
int db_val;
u64 db_valid_mask;
};
#define ntb_ndev(__ntb) container_of(__ntb, struct ntb_epf_dev, ntb)
struct ntb_epf_data {
/* BAR that contains both control region and self spad region */
enum pci_barno ctrl_reg_bar;
/* BAR that contains peer spad region */
enum pci_barno peer_spad_reg_bar;
/* BAR that contains Doorbell region and Memory window '1' */
enum pci_barno db_reg_bar;
};
static int ntb_epf_send_command(struct ntb_epf_dev *ndev, u32 command,
u32 argument)
{
ktime_t timeout;
bool timedout;
int ret = 0;
u32 status;
mutex_lock(&ndev->cmd_lock);
writel(argument, ndev->ctrl_reg + NTB_EPF_ARGUMENT);
writel(command, ndev->ctrl_reg + NTB_EPF_COMMAND);
timeout = ktime_add_ms(ktime_get(), NTB_EPF_COMMAND_TIMEOUT);
while (1) {
timedout = ktime_after(ktime_get(), timeout);
status = readw(ndev->ctrl_reg + NTB_EPF_CMD_STATUS);
if (status == COMMAND_STATUS_ERROR) {
ret = -EINVAL;
break;
}
if (status == COMMAND_STATUS_OK)
break;
if (WARN_ON(timedout)) {
ret = -ETIMEDOUT;
break;
}
usleep_range(5, 10);
}
writew(0, ndev->ctrl_reg + NTB_EPF_CMD_STATUS);
mutex_unlock(&ndev->cmd_lock);
return ret;
}
static int ntb_epf_mw_to_bar(struct ntb_epf_dev *ndev, int idx)
{
struct device *dev = ndev->dev;
if (idx < 0 || idx > ndev->mw_count) {
dev_err(dev, "Unsupported Memory Window index %d\n", idx);
return -EINVAL;
}
return idx + 2;
}
static int ntb_epf_mw_count(struct ntb_dev *ntb, int pidx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
return ndev->mw_count;
}
static int ntb_epf_mw_get_align(struct ntb_dev *ntb, int pidx, int idx,
resource_size_t *addr_align,
resource_size_t *size_align,
resource_size_t *size_max)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int bar;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
bar = ntb_epf_mw_to_bar(ndev, idx);
if (bar < 0)
return bar;
if (addr_align)
*addr_align = SZ_4K;
if (size_align)
*size_align = 1;
if (size_max)
*size_max = pci_resource_len(ndev->ntb.pdev, bar);
return 0;
}
static u64 ntb_epf_link_is_up(struct ntb_dev *ntb,
enum ntb_speed *speed,
enum ntb_width *width)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
u32 status;
status = readw(ndev->ctrl_reg + NTB_EPF_LINK_STATUS);
return status & LINK_STATUS_UP;
}
static u32 ntb_epf_spad_read(struct ntb_dev *ntb, int idx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "READ: Invalid ScratchPad Index %d\n", idx);
return 0;
}
offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
offset += (idx << 2);
return readl(ndev->ctrl_reg + offset);
}
static int ntb_epf_spad_write(struct ntb_dev *ntb,
int idx, u32 val)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "WRITE: Invalid ScratchPad Index %d\n", idx);
return -EINVAL;
}
offset = readl(ndev->ctrl_reg + NTB_EPF_SPAD_OFFSET);
offset += (idx << 2);
writel(val, ndev->ctrl_reg + offset);
return 0;
}
static u32 ntb_epf_peer_spad_read(struct ntb_dev *ntb, int pidx, int idx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx);
return -EINVAL;
}
offset = (idx << 2);
return readl(ndev->peer_spad_reg + offset);
}
static int ntb_epf_peer_spad_write(struct ntb_dev *ntb, int pidx,
int idx, u32 val)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
u32 offset;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
if (idx < 0 || idx >= ndev->spad_count) {
dev_err(dev, "WRITE: Invalid Peer ScratchPad Index %d\n", idx);
return -EINVAL;
}
offset = (idx << 2);
writel(val, ndev->peer_spad_reg + offset);
return 0;
}
static int ntb_epf_link_enable(struct ntb_dev *ntb,
enum ntb_speed max_speed,
enum ntb_width max_width)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int ret;
ret = ntb_epf_send_command(ndev, CMD_LINK_UP, 0);
if (ret) {
dev_err(dev, "Fail to enable link\n");
return ret;
}
return 0;
}
static int ntb_epf_link_disable(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int ret;
ret = ntb_epf_send_command(ndev, CMD_LINK_DOWN, 0);
if (ret) {
dev_err(dev, "Fail to disable link\n");
return ret;
}
return 0;
}
static irqreturn_t ntb_epf_vec_isr(int irq, void *dev)
{
struct ntb_epf_dev *ndev = dev;
int irq_no;
irq_no = irq - pci_irq_vector(ndev->ntb.pdev, 0);
ndev->db_val = irq_no + 1;
if (irq_no == 0)
ntb_link_event(&ndev->ntb);
else
ntb_db_event(&ndev->ntb, irq_no);
return IRQ_HANDLED;
}
static int ntb_epf_init_isr(struct ntb_epf_dev *ndev, int msi_min, int msi_max)
{
struct pci_dev *pdev = ndev->ntb.pdev;
struct device *dev = ndev->dev;
u32 argument = MSIX_ENABLE;
int irq;
int ret;
int i;
irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max, PCI_IRQ_MSIX);
if (irq < 0) {
dev_dbg(dev, "Failed to get MSIX interrupts\n");
irq = pci_alloc_irq_vectors(pdev, msi_min, msi_max,
PCI_IRQ_MSI);
if (irq < 0) {
dev_err(dev, "Failed to get MSI interrupts\n");
return irq;
}
argument &= ~MSIX_ENABLE;
}
for (i = 0; i < irq; i++) {
ret = request_irq(pci_irq_vector(pdev, i), ntb_epf_vec_isr,
0, "ntb_epf", ndev);
if (ret) {
dev_err(dev, "Failed to request irq\n");
goto err_request_irq;
}
}
ndev->db_count = irq - 1;
ret = ntb_epf_send_command(ndev, CMD_CONFIGURE_DOORBELL,
argument | irq);
if (ret) {
dev_err(dev, "Failed to configure doorbell\n");
goto err_configure_db;
}
return 0;
err_configure_db:
for (i = 0; i < ndev->db_count + 1; i++)
free_irq(pci_irq_vector(pdev, i), ndev);
err_request_irq:
pci_free_irq_vectors(pdev);
return ret;
}
static int ntb_epf_peer_mw_count(struct ntb_dev *ntb)
{
return ntb_ndev(ntb)->mw_count;
}
static int ntb_epf_spad_count(struct ntb_dev *ntb)
{
return ntb_ndev(ntb)->spad_count;
}
static u64 ntb_epf_db_valid_mask(struct ntb_dev *ntb)
{
return ntb_ndev(ntb)->db_valid_mask;
}
static int ntb_epf_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
{
return 0;
}
static int ntb_epf_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx,
dma_addr_t addr, resource_size_t size)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
resource_size_t mw_size;
int bar;
if (pidx != NTB_DEF_PEER_IDX) {
dev_err(dev, "Unsupported Peer ID %d\n", pidx);
return -EINVAL;
}
bar = idx + NTB_EPF_MW_OFFSET;
mw_size = pci_resource_len(ntb->pdev, bar);
if (size > mw_size) {
dev_err(dev, "Size:%pa is greater than the MW size %pa\n",
&size, &mw_size);
return -EINVAL;
}
writel(lower_32_bits(addr), ndev->ctrl_reg + NTB_EPF_LOWER_ADDR);
writel(upper_32_bits(addr), ndev->ctrl_reg + NTB_EPF_UPPER_ADDR);
writel(lower_32_bits(size), ndev->ctrl_reg + NTB_EPF_LOWER_SIZE);
writel(upper_32_bits(size), ndev->ctrl_reg + NTB_EPF_UPPER_SIZE);
ntb_epf_send_command(ndev, CMD_CONFIGURE_MW, idx);
return 0;
}
static int ntb_epf_mw_clear_trans(struct ntb_dev *ntb, int pidx, int idx)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
struct device *dev = ndev->dev;
int ret = 0;
ntb_epf_send_command(ndev, CMD_TEARDOWN_MW, idx);
if (ret)
dev_err(dev, "Failed to teardown memory window\n");
return ret;
}
static int ntb_epf_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
phys_addr_t *base, resource_size_t *size)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
u32 offset = 0;
int bar;
if (idx == 0)
offset = readl(ndev->ctrl_reg + NTB_EPF_MW1_OFFSET);
bar = idx + NTB_EPF_MW_OFFSET;
if (base)
*base = pci_resource_start(ndev->ntb.pdev, bar) + offset;
if (size)
*size = pci_resource_len(ndev->ntb.pdev, bar) - offset;
return 0;
}
static int ntb_epf_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
u32 interrupt_num = ffs(db_bits) + 1;
struct device *dev = ndev->dev;
u32 db_entry_size;
u32 db_offset;
u32 db_data;
if (interrupt_num > ndev->db_count) {
dev_err(dev, "DB interrupt %d greater than Max Supported %d\n",
interrupt_num, ndev->db_count);
return -EINVAL;
}
db_entry_size = readl(ndev->ctrl_reg + NTB_EPF_DB_ENTRY_SIZE);
db_data = readl(ndev->ctrl_reg + NTB_EPF_DB_DATA(interrupt_num));
db_offset = readl(ndev->ctrl_reg + NTB_EPF_DB_OFFSET(interrupt_num));
writel(db_data, ndev->db_reg + (db_entry_size * interrupt_num) +
db_offset);
return 0;
}
static u64 ntb_epf_db_read(struct ntb_dev *ntb)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
return ndev->db_val;
}
static int ntb_epf_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
{
return 0;
}
static int ntb_epf_db_clear(struct ntb_dev *ntb, u64 db_bits)
{
struct ntb_epf_dev *ndev = ntb_ndev(ntb);
ndev->db_val = 0;
return 0;
}
static const struct ntb_dev_ops ntb_epf_ops = {
.mw_count = ntb_epf_mw_count,
.spad_count = ntb_epf_spad_count,
.peer_mw_count = ntb_epf_peer_mw_count,
.db_valid_mask = ntb_epf_db_valid_mask,
.db_set_mask = ntb_epf_db_set_mask,
.mw_set_trans = ntb_epf_mw_set_trans,
.mw_clear_trans = ntb_epf_mw_clear_trans,
.peer_mw_get_addr = ntb_epf_peer_mw_get_addr,
.link_enable = ntb_epf_link_enable,
.spad_read = ntb_epf_spad_read,
.spad_write = ntb_epf_spad_write,
.peer_spad_read = ntb_epf_peer_spad_read,
.peer_spad_write = ntb_epf_peer_spad_write,
.peer_db_set = ntb_epf_peer_db_set,
.db_read = ntb_epf_db_read,
.mw_get_align = ntb_epf_mw_get_align,
.link_is_up = ntb_epf_link_is_up,
.db_clear_mask = ntb_epf_db_clear_mask,
.db_clear = ntb_epf_db_clear,
.link_disable = ntb_epf_link_disable,
};
static inline void ntb_epf_init_struct(struct ntb_epf_dev *ndev,
struct pci_dev *pdev)
{
ndev->ntb.pdev = pdev;
ndev->ntb.topo = NTB_TOPO_NONE;
ndev->ntb.ops = &ntb_epf_ops;
}
static int ntb_epf_init_dev(struct ntb_epf_dev *ndev)
{
struct device *dev = ndev->dev;
int ret;
/* One Link interrupt and rest doorbell interrupt */
ret = ntb_epf_init_isr(ndev, NTB_EPF_MIN_DB_COUNT + 1,
NTB_EPF_MAX_DB_COUNT + 1);
if (ret) {
dev_err(dev, "Failed to init ISR\n");
return ret;
}
ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1;
ndev->mw_count = readl(ndev->ctrl_reg + NTB_EPF_MW_COUNT);
ndev->spad_count = readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT);
return 0;
}
static int ntb_epf_init_pci(struct ntb_epf_dev *ndev,
struct pci_dev *pdev)
{
struct device *dev = ndev->dev;
int ret;
pci_set_drvdata(pdev, ndev);
ret = pci_enable_device(pdev);
if (ret) {
dev_err(dev, "Cannot enable PCI device\n");
goto err_pci_enable;
}
ret = pci_request_regions(pdev, "ntb");
if (ret) {
dev_err(dev, "Cannot obtain PCI resources\n");
goto err_pci_regions;
}
pci_set_master(pdev);
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
if (ret) {
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "Cannot set DMA mask\n");
goto err_dma_mask;
}
dev_warn(&pdev->dev, "Cannot DMA highmem\n");
}
ndev->ctrl_reg = pci_iomap(pdev, ndev->ctrl_reg_bar, 0);
if (!ndev->ctrl_reg) {
ret = -EIO;
goto err_dma_mask;
}
ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0);
if (!ndev->peer_spad_reg) {
ret = -EIO;
goto err_dma_mask;
}
ndev->db_reg = pci_iomap(pdev, ndev->db_reg_bar, 0);
if (!ndev->db_reg) {
ret = -EIO;
goto err_dma_mask;
}
return 0;
err_dma_mask:
pci_clear_master(pdev);
err_pci_regions:
pci_disable_device(pdev);
err_pci_enable:
pci_set_drvdata(pdev, NULL);
return ret;
}
static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev)
{
struct pci_dev *pdev = ndev->ntb.pdev;
pci_iounmap(pdev, ndev->ctrl_reg);
pci_iounmap(pdev, ndev->peer_spad_reg);
pci_iounmap(pdev, ndev->db_reg);
pci_clear_master(pdev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
}
static void ntb_epf_cleanup_isr(struct ntb_epf_dev *ndev)
{
struct pci_dev *pdev = ndev->ntb.pdev;
int i;
ntb_epf_send_command(ndev, CMD_TEARDOWN_DOORBELL, ndev->db_count + 1);
for (i = 0; i < ndev->db_count + 1; i++)
free_irq(pci_irq_vector(pdev, i), ndev);
pci_free_irq_vectors(pdev);
}
static int ntb_epf_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
enum pci_barno peer_spad_reg_bar = BAR_1;
enum pci_barno ctrl_reg_bar = BAR_0;
enum pci_barno db_reg_bar = BAR_2;
struct device *dev = &pdev->dev;
struct ntb_epf_data *data;
struct ntb_epf_dev *ndev;
int ret;
if (pci_is_bridge(pdev))
return -ENODEV;
ndev = devm_kzalloc(dev, sizeof(*ndev), GFP_KERNEL);
if (!ndev)
return -ENOMEM;
data = (struct ntb_epf_data *)id->driver_data;
if (data) {
if (data->peer_spad_reg_bar)
peer_spad_reg_bar = data->peer_spad_reg_bar;
if (data->ctrl_reg_bar)
ctrl_reg_bar = data->ctrl_reg_bar;
if (data->db_reg_bar)
db_reg_bar = data->db_reg_bar;
}
ndev->peer_spad_reg_bar = peer_spad_reg_bar;
ndev->ctrl_reg_bar = ctrl_reg_bar;
ndev->db_reg_bar = db_reg_bar;
ndev->dev = dev;
ntb_epf_init_struct(ndev, pdev);
mutex_init(&ndev->cmd_lock);
ret = ntb_epf_init_pci(ndev, pdev);
if (ret) {
dev_err(dev, "Failed to init PCI\n");
return ret;
}
ret = ntb_epf_init_dev(ndev);
if (ret) {
dev_err(dev, "Failed to init device\n");
goto err_init_dev;
}
ret = ntb_register_device(&ndev->ntb);
if (ret) {
dev_err(dev, "Failed to register NTB device\n");
goto err_register_dev;
}
return 0;
err_register_dev:
ntb_epf_cleanup_isr(ndev);
err_init_dev:
ntb_epf_deinit_pci(ndev);
return ret;
}
static void ntb_epf_pci_remove(struct pci_dev *pdev)
{
struct ntb_epf_dev *ndev = pci_get_drvdata(pdev);
ntb_unregister_device(&ndev->ntb);
ntb_epf_cleanup_isr(ndev);
ntb_epf_deinit_pci(ndev);
}
static const struct ntb_epf_data j721e_data = {
.ctrl_reg_bar = BAR_0,
.peer_spad_reg_bar = BAR_1,
.db_reg_bar = BAR_2,
};
static const struct pci_device_id ntb_epf_pci_tbl[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E),
.class = PCI_CLASS_MEMORY_RAM << 8, .class_mask = 0xffff00,
.driver_data = (kernel_ulong_t)&j721e_data,
},
{ },
};
static struct pci_driver ntb_epf_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = ntb_epf_pci_tbl,
.probe = ntb_epf_pci_probe,
.remove = ntb_epf_pci_remove,
};
module_pci_driver(ntb_epf_pci_driver);
MODULE_DESCRIPTION("PCI ENDPOINT NTB HOST DRIVER");
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
MODULE_LICENSE("GPL v2");

View File

@ -36,4 +36,4 @@ obj-$(CONFIG_PCI_ENDPOINT) += endpoint/
obj-y += controller/
obj-y += switch/
ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
subdir-ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG

View File

@ -55,15 +55,6 @@ config PCI_RCAR_GEN2
There are 3 internal PCI controllers available with a single
built-in EHCI/OHCI host controller present on each one.
config PCIE_RCAR
bool "Renesas R-Car PCIe controller"
depends on ARCH_RENESAS || COMPILE_TEST
depends on PCI_MSI_IRQ_DOMAIN
select PCIE_RCAR_HOST
help
Say Y here if you want PCIe controller support on R-Car SoCs.
This option will be removed after arm64 defconfig is updated.
config PCIE_RCAR_HOST
bool "Renesas R-Car PCIe host controller"
depends on ARCH_RENESAS || COMPILE_TEST
@ -242,20 +233,6 @@ config PCIE_MEDIATEK
Say Y here if you want to enable PCIe controller support on
MediaTek SoCs.
config PCIE_TANGO_SMP8759
bool "Tango SMP8759 PCIe controller (DANGEROUS)"
depends on ARCH_TANGO && PCI_MSI && OF
depends on BROKEN
select PCI_HOST_COMMON
help
Say Y here to enable PCIe controller support for Sigma Designs
Tango SMP8759-based systems.
Note: The SMP8759 controller multiplexes PCI config and MMIO
accesses, and Linux doesn't provide a way to serialize them.
This can lead to data corruption if drivers perform concurrent
config and MMIO accesses.
config VMD
depends on PCI_MSI && X86_64 && SRCU
tristate "Intel Volume Management Device Driver"
@ -273,7 +250,7 @@ config VMD
config PCIE_BRCMSTB
tristate "Broadcom Brcmstb PCIe host controller"
depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
depends on ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCM4908 || COMPILE_TEST
depends on OF
depends on PCI_MSI_IRQ_DOMAIN
default ARCH_BRCMSTB
@ -298,6 +275,16 @@ config PCI_LOONGSON
Say Y here if you want to enable PCI controller support on
Loongson systems.
config PCIE_MICROCHIP_HOST
bool "Microchip AXI PCIe host bridge support"
depends on PCI_MSI && OF
select PCI_MSI_IRQ_DOMAIN
select GENERIC_MSI_IRQ_DOMAIN
select PCI_HOST_COMMON
help
Say Y here if you want kernel to support the Microchip AXI PCIe
Host Bridge driver.
config PCIE_HISI_ERR
depends on ACPI_APEI_GHES && (ARM64 || COMPILE_TEST)
bool "HiSilicon HIP PCIe controller error handling driver"

View File

@ -27,7 +27,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o
obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
obj-$(CONFIG_PCIE_MICROCHIP_HOST) += pcie-microchip-host.o
obj-$(CONFIG_VMD) += vmd.o
obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o

View File

@ -64,6 +64,7 @@ enum j721e_pcie_mode {
struct j721e_pcie_data {
enum j721e_pcie_mode mode;
bool quirk_retrain_flag;
};
static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset)
@ -280,6 +281,7 @@ static struct pci_ops cdns_ti_pcie_host_ops = {
static const struct j721e_pcie_data j721e_pcie_rc_data = {
.mode = PCI_MODE_RC,
.quirk_retrain_flag = true,
};
static const struct j721e_pcie_data j721e_pcie_ep_data = {
@ -388,6 +390,7 @@ static int j721e_pcie_probe(struct platform_device *pdev)
bridge->ops = &cdns_ti_pcie_host_ops;
rc = pci_host_bridge_priv(bridge);
rc->quirk_retrain_flag = data->quirk_retrain_flag;
cdns_pcie = &rc->pcie;
cdns_pcie->dev = dev;

View File

@ -382,6 +382,57 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn,
return 0;
}
static int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn,
phys_addr_t addr, u8 interrupt_num,
u32 entry_size, u32 *msi_data,
u32 *msi_addr_offset)
{
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET;
struct cdns_pcie *pcie = &ep->pcie;
u64 pci_addr, pci_addr_mask = 0xff;
u16 flags, mme, data, data_mask;
u8 msi_count;
int ret;
int i;
/* Check whether the MSI feature has been enabled by the PCI host. */
flags = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_FLAGS);
if (!(flags & PCI_MSI_FLAGS_ENABLE))
return -EINVAL;
/* Get the number of enabled MSIs */
mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
msi_count = 1 << mme;
if (!interrupt_num || interrupt_num > msi_count)
return -EINVAL;
/* Compute the data value to be written. */
data_mask = msi_count - 1;
data = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_MSI_DATA_64);
data = data & ~data_mask;
/* Get the PCI address where to write the data into. */
pci_addr = cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_HI);
pci_addr <<= 32;
pci_addr |= cdns_pcie_ep_fn_readl(pcie, fn, cap + PCI_MSI_ADDRESS_LO);
pci_addr &= GENMASK_ULL(63, 2);
for (i = 0; i < interrupt_num; i++) {
ret = cdns_pcie_ep_map_addr(epc, fn, addr,
pci_addr & ~pci_addr_mask,
entry_size);
if (ret)
return ret;
addr = addr + entry_size;
}
*msi_data = data;
*msi_addr_offset = pci_addr & pci_addr_mask;
return 0;
}
static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn,
u16 interrupt_num)
{
@ -455,18 +506,13 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
struct cdns_pcie *pcie = &ep->pcie;
struct device *dev = pcie->dev;
struct pci_epf *epf;
u32 cfg;
int ret;
/*
* BIT(0) is hardwired to 1, hence function 0 is always enabled
* and can't be disabled anyway.
*/
cfg = BIT(0);
list_for_each_entry(epf, &epc->pci_epf, list)
cfg |= BIT(epf->func_no);
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, epc->function_num_map);
ret = cdns_pcie_start_link(pcie);
if (ret) {
@ -481,6 +527,7 @@ static const struct pci_epc_features cdns_pcie_epc_features = {
.linkup_notifier = false,
.msi_capable = true,
.msix_capable = true,
.align = 256,
};
static const struct pci_epc_features*
@ -500,6 +547,7 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
.set_msix = cdns_pcie_ep_set_msix,
.get_msix = cdns_pcie_ep_get_msix,
.raise_irq = cdns_pcie_ep_raise_irq,
.map_msi_irq = cdns_pcie_ep_map_msi_irq,
.start = cdns_pcie_ep_start,
.get_features = cdns_pcie_ep_get_features,
};

View File

@ -77,6 +77,68 @@ static struct pci_ops cdns_pcie_host_ops = {
.write = pci_generic_config_write,
};
static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
{
struct device *dev = pcie->dev;
int retries;
/* Check if the link is up or not */
for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
if (cdns_pcie_link_up(pcie)) {
dev_info(dev, "Link up\n");
return 0;
}
usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
}
return -ETIMEDOUT;
}
static int cdns_pcie_retrain(struct cdns_pcie *pcie)
{
u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
u16 lnk_stat, lnk_ctl;
int ret = 0;
/*
* Set retrain bit if current speed is 2.5 GB/s,
* but the PCIe root port support is > 2.5 GB/s.
*/
lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off +
PCI_EXP_LNKCAP));
if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
return ret;
lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
lnk_ctl = cdns_pcie_rp_readw(pcie,
pcie_cap_off + PCI_EXP_LNKCTL);
lnk_ctl |= PCI_EXP_LNKCTL_RL;
cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL,
lnk_ctl);
ret = cdns_pcie_host_wait_for_link(pcie);
}
return ret;
}
static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc)
{
struct cdns_pcie *pcie = &rc->pcie;
int ret;
ret = cdns_pcie_host_wait_for_link(pcie);
/*
* Retrain link for Gen2 training defect
* if quirk flag is set.
*/
if (!ret && rc->quirk_retrain_flag)
ret = cdns_pcie_retrain(pcie);
return ret;
}
static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
{
@ -321,9 +383,10 @@ static int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc)
resource_list_for_each_entry(entry, &bridge->dma_ranges) {
err = cdns_pcie_host_bar_config(rc, entry);
if (err)
if (err) {
dev_err(dev, "Fail to configure IB using dma-ranges\n");
return err;
return err;
}
}
return 0;
@ -398,23 +461,6 @@ static int cdns_pcie_host_init(struct device *dev,
return cdns_pcie_host_init_address_translation(rc);
}
static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
{
struct device *dev = pcie->dev;
int retries;
/* Check if the link is up or not */
for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
if (cdns_pcie_link_up(pcie)) {
dev_info(dev, "Link up\n");
return 0;
}
usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
}
return -ETIMEDOUT;
}
int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
{
struct device *dev = rc->pcie.dev;
@ -457,7 +503,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
return ret;
}
ret = cdns_pcie_host_wait_for_link(pcie);
ret = cdns_pcie_host_start_link(rc);
if (ret)
dev_dbg(dev, "PCIe link never came up\n");

View File

@ -119,7 +119,7 @@
* Root Port Registers (PCI configuration space for the root port function)
*/
#define CDNS_PCIE_RP_BASE 0x00200000
#define CDNS_PCIE_RP_CAP_OFFSET 0xc0
/*
* Address Translation Registers
@ -291,6 +291,7 @@ struct cdns_pcie {
* @device_id: PCI device ID
* @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 and RP_NO_BAR if it's free or
* available
* @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2
*/
struct cdns_pcie_rc {
struct cdns_pcie pcie;
@ -299,6 +300,7 @@ struct cdns_pcie_rc {
u32 vendor_id;
u32 device_id;
bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
bool quirk_retrain_flag;
};
/**
@ -414,6 +416,13 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie,
cdns_pcie_write_sz(addr, 0x2, value);
}
static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg)
{
void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg;
return cdns_pcie_read_sz(addr, 0x2);
}
/* Endpoint Function register access */
static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn,
u32 reg, u8 value)

View File

@ -115,10 +115,17 @@ static const struct ls_pcie_ep_drvdata ls2_ep_drvdata = {
.dw_pcie_ops = &dw_ls_pcie_ep_ops,
};
static const struct ls_pcie_ep_drvdata lx2_ep_drvdata = {
.func_offset = 0x8000,
.ops = &ls_pcie_ep_ops,
.dw_pcie_ops = &dw_ls_pcie_ep_ops,
};
static const struct of_device_id ls_pcie_ep_of_match[] = {
{ .compatible = "fsl,ls1046a-pcie-ep", .data = &ls1_ep_drvdata },
{ .compatible = "fsl,ls1088a-pcie-ep", .data = &ls2_ep_drvdata },
{ .compatible = "fsl,ls2088a-pcie-ep", .data = &ls2_ep_drvdata },
{ .compatible = "fsl,lx2160ar2-pcie-ep", .data = &lx2_ep_drvdata },
{ },
};

View File

@ -232,7 +232,7 @@ static const struct of_device_id ls_pcie_of_match[] = {
{ },
};
static int __init ls_pcie_probe(struct platform_device *pdev)
static int ls_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_pcie *pci;
@ -271,10 +271,11 @@ static int __init ls_pcie_probe(struct platform_device *pdev)
}
static struct platform_driver ls_pcie_driver = {
.probe = ls_pcie_probe,
.driver = {
.name = "layerscape-pcie",
.of_match_table = ls_pcie_of_match,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
builtin_platform_driver(ls_pcie_driver);

View File

@ -314,9 +314,6 @@ static const struct dw_pcie_host_ops al_pcie_host_ops = {
.host_init = al_pcie_host_init,
};
static const struct dw_pcie_ops dw_pcie_ops = {
};
static int al_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -334,7 +331,6 @@ static int al_pcie_probe(struct platform_device *pdev)
return -ENOMEM;
pci->dev = dev;
pci->ops = &dw_pcie_ops;
pci->pp.ops = &al_pcie_host_ops;
al_pcie->pci = pci;

View File

@ -434,10 +434,8 @@ static void dw_pcie_ep_stop(struct pci_epc *epc)
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
if (!pci->ops->stop_link)
return;
pci->ops->stop_link(pci);
if (pci->ops && pci->ops->stop_link)
pci->ops->stop_link(pci);
}
static int dw_pcie_ep_start(struct pci_epc *epc)
@ -445,7 +443,7 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
if (!pci->ops->start_link)
if (!pci->ops || !pci->ops->start_link)
return -EINVAL;
return pci->ops->start_link(pci);

View File

@ -258,10 +258,8 @@ int dw_pcie_allocate_domains(struct pcie_port *pp)
static void dw_pcie_free_msi(struct pcie_port *pp)
{
if (pp->msi_irq) {
irq_set_chained_handler(pp->msi_irq, NULL);
irq_set_handler_data(pp->msi_irq, NULL);
}
if (pp->msi_irq)
irq_set_chained_handler_and_data(pp->msi_irq, NULL, NULL);
irq_domain_remove(pp->msi_domain);
irq_domain_remove(pp->irq_domain);
@ -305,8 +303,13 @@ int dw_pcie_host_init(struct pcie_port *pp)
if (cfg_res) {
pp->cfg0_size = resource_size(cfg_res);
pp->cfg0_base = cfg_res->start;
} else if (!pp->va_cfg0_base) {
pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, cfg_res);
if (IS_ERR(pp->va_cfg0_base))
return PTR_ERR(pp->va_cfg0_base);
} else {
dev_err(dev, "Missing *config* reg space\n");
return -ENODEV;
}
if (!pci->dbi_base) {
@ -322,38 +325,12 @@ int dw_pcie_host_init(struct pcie_port *pp)
pp->bridge = bridge;
/* Get the I/O and memory ranges from DT */
resource_list_for_each_entry(win, &bridge->windows) {
switch (resource_type(win->res)) {
case IORESOURCE_IO:
pp->io_size = resource_size(win->res);
pp->io_bus_addr = win->res->start - win->offset;
pp->io_base = pci_pio_to_address(win->res->start);
break;
case 0:
dev_err(dev, "Missing *config* reg space\n");
pp->cfg0_size = resource_size(win->res);
pp->cfg0_base = win->res->start;
if (!pci->dbi_base) {
pci->dbi_base = devm_pci_remap_cfgspace(dev,
pp->cfg0_base,
pp->cfg0_size);
if (!pci->dbi_base) {
dev_err(dev, "Error with ioremap\n");
return -ENOMEM;
}
}
break;
}
}
if (!pp->va_cfg0_base) {
pp->va_cfg0_base = devm_pci_remap_cfgspace(dev,
pp->cfg0_base, pp->cfg0_size);
if (!pp->va_cfg0_base) {
dev_err(dev, "Error with ioremap in function\n");
return -ENOMEM;
}
/* Get the I/O range from DT */
win = resource_list_first_type(&bridge->windows, IORESOURCE_IO);
if (win) {
pp->io_size = resource_size(win->res);
pp->io_bus_addr = win->res->start - win->offset;
pp->io_base = pci_pio_to_address(win->res->start);
}
if (pci->link_gen < 1)
@ -425,7 +402,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
dw_pcie_setup_rc(pp);
dw_pcie_msi_init(pp);
if (!dw_pcie_link_up(pci) && pci->ops->start_link) {
if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
ret = pci->ops->start_link(pci);
if (ret)
goto err_free_msi;

View File

@ -141,7 +141,7 @@ u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size)
int ret;
u32 val;
if (pci->ops->read_dbi)
if (pci->ops && pci->ops->read_dbi)
return pci->ops->read_dbi(pci, pci->dbi_base, reg, size);
ret = dw_pcie_read(pci->dbi_base + reg, size, &val);
@ -156,7 +156,7 @@ void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
{
int ret;
if (pci->ops->write_dbi) {
if (pci->ops && pci->ops->write_dbi) {
pci->ops->write_dbi(pci, pci->dbi_base, reg, size, val);
return;
}
@ -171,7 +171,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
{
int ret;
if (pci->ops->write_dbi2) {
if (pci->ops && pci->ops->write_dbi2) {
pci->ops->write_dbi2(pci, pci->dbi_base2, reg, size, val);
return;
}
@ -186,7 +186,7 @@ static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg)
int ret;
u32 val;
if (pci->ops->read_dbi)
if (pci->ops && pci->ops->read_dbi)
return pci->ops->read_dbi(pci, pci->atu_base, reg, 4);
ret = dw_pcie_read(pci->atu_base + reg, 4, &val);
@ -200,7 +200,7 @@ static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val)
{
int ret;
if (pci->ops->write_dbi) {
if (pci->ops && pci->ops->write_dbi) {
pci->ops->write_dbi(pci, pci->atu_base, reg, 4, val);
return;
}
@ -225,6 +225,47 @@ static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg,
dw_pcie_writel_atu(pci, offset + reg, val);
}
static inline u32 dw_pcie_enable_ecrc(u32 val)
{
/*
* DesignWare core version 4.90A has a design issue where the 'TD'
* bit in the Control register-1 of the ATU outbound region acts
* like an override for the ECRC setting, i.e., the presence of TLP
* Digest (ECRC) in the outgoing TLPs is solely determined by this
* bit. This is contrary to the PCIe spec which says that the
* enablement of the ECRC is solely determined by the AER
* registers.
*
* Because of this, even when the ECRC is enabled through AER
* registers, the transactions going through ATU won't have TLP
* Digest as there is no way the PCI core AER code could program
* the TD bit which is specific to the DesignWare core.
*
* The best way to handle this scenario is to program the TD bit
* always. It affects only the traffic from root port to downstream
* devices.
*
* At this point,
* When ECRC is enabled in AER registers, everything works normally
* When ECRC is NOT enabled in AER registers, then,
* on Root Port:- TLP Digest (DWord size) gets appended to each packet
* even through it is not required. Since downstream
* TLPs are mostly for configuration accesses and BAR
* accesses, they are not in critical path and won't
* have much negative effect on the performance.
* on End Point:- TLP Digest is received for some/all the packets coming
* from the root port. TLP Digest is ignored because,
* as per the PCIe Spec r5.0 v1.0 section 2.2.3
* "TLP Digest Rules", when an endpoint receives TLP
* Digest when its ECRC check functionality is disabled
* in AER registers, received TLP Digest is just ignored.
* Since there is no issue or error reported either side, best way to
* handle the scenario is to program TD bit by default.
*/
return val | PCIE_ATU_TD;
}
static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
int index, int type,
u64 cpu_addr, u64 pci_addr,
@ -248,6 +289,8 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
val = type | PCIE_ATU_FUNC_NUM(func_no);
val = upper_32_bits(size - 1) ?
val | PCIE_ATU_INCREASE_REGION_SIZE : val;
if (pci->version == 0x490A)
val = dw_pcie_enable_ecrc(val);
dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
PCIE_ATU_ENABLE);
@ -273,7 +316,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
{
u32 retries, val;
if (pci->ops->cpu_addr_fixup)
if (pci->ops && pci->ops->cpu_addr_fixup)
cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);
if (pci->iatu_unroll_enabled) {
@ -290,12 +333,19 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
upper_32_bits(cpu_addr));
dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
lower_32_bits(cpu_addr + size - 1));
if (pci->version >= 0x460A)
dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
upper_32_bits(cpu_addr + size - 1));
dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
lower_32_bits(pci_addr));
dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
upper_32_bits(pci_addr));
dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
PCIE_ATU_FUNC_NUM(func_no));
val = type | PCIE_ATU_FUNC_NUM(func_no);
val = ((upper_32_bits(size - 1)) && (pci->version >= 0x460A)) ?
val | PCIE_ATU_INCREASE_REGION_SIZE : val;
if (pci->version == 0x490A)
val = dw_pcie_enable_ecrc(val);
dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
/*
@ -321,7 +371,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
int type, u64 cpu_addr, u64 pci_addr,
u32 size)
u64 size)
{
__dw_pcie_prog_outbound_atu(pci, func_no, index, type,
cpu_addr, pci_addr, size);
@ -481,7 +531,7 @@ int dw_pcie_link_up(struct dw_pcie *pci)
{
u32 val;
if (pci->ops->link_up)
if (pci->ops && pci->ops->link_up)
return pci->ops->link_up(pci);
val = readl(pci->dbi_base + PCIE_PORT_DEBUG1);

View File

@ -86,6 +86,7 @@
#define PCIE_ATU_TYPE_IO 0x2
#define PCIE_ATU_TYPE_CFG0 0x4
#define PCIE_ATU_TYPE_CFG1 0x5
#define PCIE_ATU_TD BIT(8)
#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
#define PCIE_ATU_CR2 0x908
#define PCIE_ATU_ENABLE BIT(31)
@ -99,6 +100,7 @@
#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 0x91C
#define PCIE_ATU_UPPER_LIMIT 0x924
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
#define PCIE_DBI_RO_WR_EN BIT(0)
@ -297,7 +299,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
u64 size);
void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
int type, u64 cpu_addr, u64 pci_addr,
u32 size);
u64 size);
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
int bar, u64 cpu_addr,
enum dw_pcie_as_type as_type);

View File

@ -159,8 +159,10 @@ struct qcom_pcie_resources_2_3_3 {
struct reset_control *rst[7];
};
/* 6 clocks typically, 7 for sm8250 */
struct qcom_pcie_resources_2_7_0 {
struct clk_bulk_data clks[6];
struct clk_bulk_data clks[7];
int num_clks;
struct regulator_bulk_data supplies[2];
struct reset_control *pci_reset;
struct clk *pipe_clk;
@ -398,7 +400,9 @@ static int qcom_pcie_init_2_1_0(struct qcom_pcie *pcie)
/* enable external reference clock */
val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK);
val &= ~PHY_REFCLK_USE_PAD;
/* USE_PAD is required only for ipq806x */
if (!of_device_is_compatible(node, "qcom,pcie-apq8064"))
val &= ~PHY_REFCLK_USE_PAD;
val |= PHY_REFCLK_SSP_EN;
writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK);
@ -1152,8 +1156,14 @@ static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie)
res->clks[3].id = "bus_slave";
res->clks[4].id = "slave_q2a";
res->clks[5].id = "tbu";
if (of_device_is_compatible(dev->of_node, "qcom,pcie-sm8250")) {
res->clks[6].id = "ddrss_sf_tbu";
res->num_clks = 7;
} else {
res->num_clks = 6;
}
ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
ret = devm_clk_bulk_get(dev, res->num_clks, res->clks);
if (ret < 0)
return ret;
@ -1175,7 +1185,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
return ret;
}
ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
ret = clk_bulk_prepare_enable(res->num_clks, res->clks);
if (ret < 0)
goto err_disable_regulators;
@ -1227,7 +1237,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
return 0;
err_disable_clocks:
clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
clk_bulk_disable_unprepare(res->num_clks, res->clks);
err_disable_regulators:
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
@ -1238,7 +1248,7 @@ static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
clk_bulk_disable_unprepare(res->num_clks, res->clks);
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
}

View File

@ -64,6 +64,8 @@ int pci_host_common_probe(struct platform_device *pdev)
if (!bridge)
return -ENOMEM;
platform_set_drvdata(pdev, bridge);
of_pci_check_probe_only();
/* Parse and map our Configuration Space windows */
@ -78,8 +80,6 @@ int pci_host_common_probe(struct platform_device *pdev)
bridge->sysdata = cfg;
bridge->ops = (struct pci_ops *)&ops->pci_ops;
platform_set_drvdata(pdev, bridge);
return pci_host_probe(bridge);
}
EXPORT_SYMBOL_GPL(pci_host_common_probe);

View File

@ -1714,7 +1714,7 @@ static void prepopulate_bars(struct hv_pcibus_device *hbus)
* resumed and suspended again: see hibernation_snapshot() and
* hibernation_platform_enter().
*
* If the memory enable bit is already set, Hyper-V sliently ignores
* If the memory enable bit is already set, Hyper-V silently ignores
* the below BAR updates, and the related PCI device driver can not
* work, because reading from the device register(s) always returns
* 0xFFFFFFFF.

View File

@ -384,13 +384,9 @@ static int xgene_msi_hwirq_alloc(unsigned int cpu)
if (!msi_group->gic_irq)
continue;
irq_set_chained_handler(msi_group->gic_irq,
xgene_msi_isr);
err = irq_set_handler_data(msi_group->gic_irq, msi_group);
if (err) {
pr_err("failed to register GIC IRQ handler\n");
return -EINVAL;
}
irq_set_chained_handler_and_data(msi_group->gic_irq,
xgene_msi_isr, msi_group);
/*
* Statically allocate MSI GIC IRQs to each CPU core.
* With 8-core X-Gene v1, 2 MSI GIC IRQs are allocated

View File

@ -173,12 +173,13 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
/*
* The v1 controller has a bug in its Configuration Request
* Retry Status (CRS) logic: when CRS is enabled and we read the
* Vendor and Device ID of a non-existent device, the controller
* fabricates return data of 0xFFFF0001 ("device exists but is not
* ready") instead of 0xFFFFFFFF ("device does not exist"). This
* causes the PCI core to retry the read until it times out.
* Avoid this by not claiming to support CRS.
* Retry Status (CRS) logic: when CRS Software Visibility is
* enabled and we read the Vendor and Device ID of a non-existent
* device, the controller fabricates return data of 0xFFFF0001
* ("device exists but is not ready") instead of 0xFFFFFFFF
* ("device does not exist"). This causes the PCI core to retry
* the read until it times out. Avoid this by not claiming to
* support CRS SV.
*/
if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
((where & ~0x3) == XGENE_V1_PCI_EXP_CAP + PCI_EXP_RTCTL))

View File

@ -204,8 +204,7 @@ static int altera_msi_remove(struct platform_device *pdev)
struct altera_msi *msi = platform_get_drvdata(pdev);
msi_writel(msi, 0, MSI_INTMASK);
irq_set_chained_handler(msi->irq, NULL);
irq_set_handler_data(msi->irq, NULL);
irq_set_chained_handler_and_data(msi->irq, NULL, NULL);
altera_free_domains(msi);

View File

@ -97,6 +97,7 @@
#define PCIE_MISC_REVISION 0x406c
#define BRCM_PCIE_HW_REV_33 0x0303
#define BRCM_PCIE_HW_REV_3_20 0x0320
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070
#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000
@ -187,6 +188,7 @@
struct brcm_pcie;
static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val);
static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val);
static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val);
static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val);
static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val);
@ -203,6 +205,7 @@ enum {
enum pcie_type {
GENERIC,
BCM4908,
BCM7278,
BCM2711,
};
@ -227,6 +230,13 @@ static const struct pcie_cfg_data generic_cfg = {
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
};
static const struct pcie_cfg_data bcm4908_cfg = {
.offsets = pcie_offsets,
.type = BCM4908,
.perst_set = brcm_pcie_perst_set_4908,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
};
static const int pcie_offset_bcm7278[] = {
[RGR1_SW_INIT_1] = 0xc010,
[EXT_CFG_INDEX] = 0x9000,
@ -279,6 +289,7 @@ struct brcm_pcie {
const int *reg_offsets;
enum pcie_type type;
struct reset_control *rescal;
struct reset_control *perst_reset;
int num_memc;
u64 memc_size[PCIE_BRCM_MAX_MEMC];
u32 hw_rev;
@ -603,8 +614,7 @@ static void brcm_msi_remove(struct brcm_pcie *pcie)
if (!msi)
return;
irq_set_chained_handler(msi->irq, NULL);
irq_set_handler_data(msi->irq, NULL);
irq_set_chained_handler_and_data(msi->irq, NULL, NULL);
brcm_free_domains(msi);
}
@ -735,6 +745,17 @@ static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32
writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
}
static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
{
if (WARN_ONCE(!pcie->perst_reset, "missing PERST# reset controller\n"))
return;
if (val)
reset_control_assert(pcie->perst_reset);
else
reset_control_deassert(pcie->perst_reset);
}
static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
{
u32 tmp;
@ -1194,6 +1215,7 @@ static int brcm_pcie_remove(struct platform_device *pdev)
static const struct of_device_id brcm_pcie_match[] = {
{ .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
{ .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg },
{ .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
{ .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg },
{ .compatible = "brcm,bcm7216-pcie", .data = &bcm7278_cfg },
@ -1250,6 +1272,11 @@ static int brcm_pcie_probe(struct platform_device *pdev)
clk_disable_unprepare(pcie->clk);
return PTR_ERR(pcie->rescal);
}
pcie->perst_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "perst");
if (IS_ERR(pcie->perst_reset)) {
clk_disable_unprepare(pcie->clk);
return PTR_ERR(pcie->perst_reset);
}
ret = reset_control_deassert(pcie->rescal);
if (ret)
@ -1267,6 +1294,10 @@ static int brcm_pcie_probe(struct platform_device *pdev)
goto fail;
pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION);
if (pcie->type == BCM4908 && pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) {
dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n");
goto fail;
}
msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
if (pci_msi_enabled() && msi_np == pcie->np) {

View File

@ -1035,14 +1035,14 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
err = of_pci_get_devfn(child);
if (err < 0) {
dev_err(dev, "failed to parse devfn: %d\n", err);
return err;
goto error_put_node;
}
slot = PCI_SLOT(err);
err = mtk_pcie_parse_port(pcie, child, slot);
if (err)
return err;
goto error_put_node;
}
err = mtk_pcie_subsys_powerup(pcie);
@ -1058,6 +1058,9 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
mtk_pcie_subsys_powerdown(pcie);
return 0;
error_put_node:
of_node_put(child);
return err;
}
static int mtk_pcie_probe(struct platform_device *pdev)

File diff suppressed because it is too large Load Diff

View File

@ -735,7 +735,7 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host)
}
/* setup MSI data target */
msi->pages = __get_free_pages(GFP_KERNEL, 0);
msi->pages = __get_free_pages(GFP_KERNEL | GFP_DMA32, 0);
rcar_pcie_hw_enable_msi(host);
return 0;

View File

@ -82,7 +82,7 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
}
rockchip->mgmt_sticky_rst = devm_reset_control_get_exclusive(dev,
"mgmt-sticky");
"mgmt-sticky");
if (IS_ERR(rockchip->mgmt_sticky_rst)) {
if (PTR_ERR(rockchip->mgmt_sticky_rst) != -EPROBE_DEFER)
dev_err(dev, "missing mgmt-sticky reset property in node\n");
@ -118,11 +118,11 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
}
if (rockchip->is_rc) {
rockchip->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
if (IS_ERR(rockchip->ep_gpio)) {
dev_err(dev, "missing ep-gpios property in node\n");
return PTR_ERR(rockchip->ep_gpio);
}
rockchip->ep_gpio = devm_gpiod_get_optional(dev, "ep",
GPIOD_OUT_HIGH);
if (IS_ERR(rockchip->ep_gpio))
return dev_err_probe(dev, PTR_ERR(rockchip->ep_gpio),
"failed to get ep GPIO\n");
}
rockchip->aclk_pcie = devm_clk_get(dev, "aclk");

View File

@ -1,341 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/pci-ecam.h>
#include <linux/delay.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#define MSI_MAX 256
#define SMP8759_MUX 0x48
#define SMP8759_TEST_OUT 0x74
#define SMP8759_DOORBELL 0x7c
#define SMP8759_STATUS 0x80
#define SMP8759_ENABLE 0xa0
struct tango_pcie {
DECLARE_BITMAP(used_msi, MSI_MAX);
u64 msi_doorbell;
spinlock_t used_msi_lock;
void __iomem *base;
struct irq_domain *dom;
};
static void tango_msi_isr(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct tango_pcie *pcie = irq_desc_get_handler_data(desc);
unsigned long status, base, virq, idx, pos = 0;
chained_irq_enter(chip, desc);
spin_lock(&pcie->used_msi_lock);
while ((pos = find_next_bit(pcie->used_msi, MSI_MAX, pos)) < MSI_MAX) {
base = round_down(pos, 32);
status = readl_relaxed(pcie->base + SMP8759_STATUS + base / 8);
for_each_set_bit(idx, &status, 32) {
virq = irq_find_mapping(pcie->dom, base + idx);
generic_handle_irq(virq);
}
pos = base + 32;
}
spin_unlock(&pcie->used_msi_lock);
chained_irq_exit(chip, desc);
}
static void tango_ack(struct irq_data *d)
{
struct tango_pcie *pcie = d->chip_data;
u32 offset = (d->hwirq / 32) * 4;
u32 bit = BIT(d->hwirq % 32);
writel_relaxed(bit, pcie->base + SMP8759_STATUS + offset);
}
static void update_msi_enable(struct irq_data *d, bool unmask)
{
unsigned long flags;
struct tango_pcie *pcie = d->chip_data;
u32 offset = (d->hwirq / 32) * 4;
u32 bit = BIT(d->hwirq % 32);
u32 val;
spin_lock_irqsave(&pcie->used_msi_lock, flags);
val = readl_relaxed(pcie->base + SMP8759_ENABLE + offset);
val = unmask ? val | bit : val & ~bit;
writel_relaxed(val, pcie->base + SMP8759_ENABLE + offset);
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
}
static void tango_mask(struct irq_data *d)
{
update_msi_enable(d, false);
}
static void tango_unmask(struct irq_data *d)
{
update_msi_enable(d, true);
}
static int tango_set_affinity(struct irq_data *d, const struct cpumask *mask,
bool force)
{
return -EINVAL;
}
static void tango_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
{
struct tango_pcie *pcie = d->chip_data;
msg->address_lo = lower_32_bits(pcie->msi_doorbell);
msg->address_hi = upper_32_bits(pcie->msi_doorbell);
msg->data = d->hwirq;
}
static struct irq_chip tango_chip = {
.irq_ack = tango_ack,
.irq_mask = tango_mask,
.irq_unmask = tango_unmask,
.irq_set_affinity = tango_set_affinity,
.irq_compose_msi_msg = tango_compose_msi_msg,
};
static void msi_ack(struct irq_data *d)
{
irq_chip_ack_parent(d);
}
static void msi_mask(struct irq_data *d)
{
pci_msi_mask_irq(d);
irq_chip_mask_parent(d);
}
static void msi_unmask(struct irq_data *d)
{
pci_msi_unmask_irq(d);
irq_chip_unmask_parent(d);
}
static struct irq_chip msi_chip = {
.name = "MSI",
.irq_ack = msi_ack,
.irq_mask = msi_mask,
.irq_unmask = msi_unmask,
};
static struct msi_domain_info msi_dom_info = {
.flags = MSI_FLAG_PCI_MSIX
| MSI_FLAG_USE_DEF_DOM_OPS
| MSI_FLAG_USE_DEF_CHIP_OPS,
.chip = &msi_chip,
};
static int tango_irq_domain_alloc(struct irq_domain *dom, unsigned int virq,
unsigned int nr_irqs, void *args)
{
struct tango_pcie *pcie = dom->host_data;
unsigned long flags;
int pos;
spin_lock_irqsave(&pcie->used_msi_lock, flags);
pos = find_first_zero_bit(pcie->used_msi, MSI_MAX);
if (pos >= MSI_MAX) {
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
return -ENOSPC;
}
__set_bit(pos, pcie->used_msi);
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
irq_domain_set_info(dom, virq, pos, &tango_chip,
pcie, handle_edge_irq, NULL, NULL);
return 0;
}
static void tango_irq_domain_free(struct irq_domain *dom, unsigned int virq,
unsigned int nr_irqs)
{
unsigned long flags;
struct irq_data *d = irq_domain_get_irq_data(dom, virq);
struct tango_pcie *pcie = d->chip_data;
spin_lock_irqsave(&pcie->used_msi_lock, flags);
__clear_bit(d->hwirq, pcie->used_msi);
spin_unlock_irqrestore(&pcie->used_msi_lock, flags);
}
static const struct irq_domain_ops dom_ops = {
.alloc = tango_irq_domain_alloc,
.free = tango_irq_domain_free,
};
static int smp8759_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
struct pci_config_window *cfg = bus->sysdata;
struct tango_pcie *pcie = dev_get_drvdata(cfg->parent);
int ret;
/* Reads in configuration space outside devfn 0 return garbage */
if (devfn != 0)
return PCIBIOS_FUNC_NOT_SUPPORTED;
/*
* PCI config and MMIO accesses are muxed. Linux doesn't have a
* mutual exclusion mechanism for config vs. MMIO accesses, so
* concurrent accesses may cause corruption.
*/
writel_relaxed(1, pcie->base + SMP8759_MUX);
ret = pci_generic_config_read(bus, devfn, where, size, val);
writel_relaxed(0, pcie->base + SMP8759_MUX);
return ret;
}
static int smp8759_config_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct pci_config_window *cfg = bus->sysdata;
struct tango_pcie *pcie = dev_get_drvdata(cfg->parent);
int ret;
writel_relaxed(1, pcie->base + SMP8759_MUX);
ret = pci_generic_config_write(bus, devfn, where, size, val);
writel_relaxed(0, pcie->base + SMP8759_MUX);
return ret;
}
static const struct pci_ecam_ops smp8759_ecam_ops = {
.pci_ops = {
.map_bus = pci_ecam_map_bus,
.read = smp8759_config_read,
.write = smp8759_config_write,
}
};
static int tango_pcie_link_up(struct tango_pcie *pcie)
{
void __iomem *test_out = pcie->base + SMP8759_TEST_OUT;
int i;
writel_relaxed(16, test_out);
for (i = 0; i < 10; ++i) {
u32 ltssm_state = readl_relaxed(test_out) >> 8;
if ((ltssm_state & 0x1f) == 0xf) /* L0 */
return 1;
usleep_range(3000, 4000);
}
return 0;
}
static int tango_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tango_pcie *pcie;
struct resource *res;
struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
struct irq_domain *msi_dom, *irq_dom;
struct of_pci_range_parser parser;
struct of_pci_range range;
int virq, offset;
dev_warn(dev, "simultaneous PCI config and MMIO accesses may cause data corruption\n");
add_taint(TAINT_CRAP, LOCKDEP_STILL_OK);
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
pcie->base = devm_ioremap_resource(dev, res);
if (IS_ERR(pcie->base))
return PTR_ERR(pcie->base);
platform_set_drvdata(pdev, pcie);
if (!tango_pcie_link_up(pcie))
return -ENODEV;
if (of_pci_dma_range_parser_init(&parser, dev->of_node) < 0)
return -ENOENT;
if (of_pci_range_parser_one(&parser, &range) == NULL)
return -ENOENT;
range.pci_addr += range.size;
pcie->msi_doorbell = range.pci_addr + res->start + SMP8759_DOORBELL;
for (offset = 0; offset < MSI_MAX / 8; offset += 4)
writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset);
virq = platform_get_irq(pdev, 1);
if (virq < 0)
return virq;
irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie);
if (!irq_dom) {
dev_err(dev, "Failed to create IRQ domain\n");
return -ENOMEM;
}
msi_dom = pci_msi_create_irq_domain(fwnode, &msi_dom_info, irq_dom);
if (!msi_dom) {
dev_err(dev, "Failed to create MSI domain\n");
irq_domain_remove(irq_dom);
return -ENOMEM;
}
pcie->dom = irq_dom;
spin_lock_init(&pcie->used_msi_lock);
irq_set_chained_handler_and_data(virq, tango_msi_isr, pcie);
return pci_host_common_probe(pdev);
}
static const struct of_device_id tango_pcie_ids[] = {
{
.compatible = "sigma,smp8759-pcie",
.data = &smp8759_ecam_ops,
},
{ },
};
static struct platform_driver tango_pcie_driver = {
.probe = tango_pcie_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = tango_pcie_ids,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(tango_pcie_driver);
/*
* The root complex advertises the wrong device class.
* Header Type 1 is for PCI-to-PCI bridges.
*/
static void tango_fixup_class(struct pci_dev *dev)
{
dev->class = PCI_CLASS_BRIDGE_PCI << 8;
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_class);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_class);
/*
* The root complex exposes a "fake" BAR, which is used to filter
* bus-to-system accesses. Only accesses within the range defined by this
* BAR are forwarded to the host, others are ignored.
*
* By default, the DMA framework expects an identity mapping, and DRAM0 is
* mapped at 0x80000000.
*/
static void tango_fixup_bar(struct pci_dev *dev)
{
dev->non_compliant_bars = true;
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x80000000);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0024, tango_fixup_bar);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x0028, tango_fixup_bar);

View File

@ -404,6 +404,7 @@ static int xilinx_cpm_pcie_init_irq_domain(struct xilinx_cpm_pcie_port *port)
return 0;
out:
xilinx_cpm_free_irq_domains(port);
of_node_put(pcie_intc_node);
dev_err(dev, "Failed to allocate IRQ domains\n");
return -ENOMEM;

View File

@ -12,3 +12,16 @@ config PCI_EPF_TEST
for PCI Endpoint.
If in doubt, say "N" to disable Endpoint test driver.
config PCI_EPF_NTB
tristate "PCI Endpoint NTB driver"
depends on PCI_ENDPOINT
select CONFIGFS_FS
help
Select this configuration option to enable the Non-Transparent
Bridge (NTB) driver for PCI Endpoint. NTB driver implements NTB
controller functionality using multiple PCIe endpoint instances.
It can support NTB endpoint function devices created using
device tree.
If in doubt, say "N" to disable Endpoint NTB driver.

View File

@ -4,3 +4,4 @@
#
obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o
obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o

File diff suppressed because it is too large Load Diff

View File

@ -619,7 +619,8 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
if (epf_test->reg[bar]) {
pci_epc_clear_bar(epc, epf->func_no, epf_bar);
pci_epf_free_space(epf, epf_test->reg[bar], bar);
pci_epf_free_space(epf, epf_test->reg[bar], bar,
PRIMARY_INTERFACE);
}
}
}
@ -651,7 +652,8 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
if (ret) {
pci_epf_free_space(epf, epf_test->reg[bar], bar);
pci_epf_free_space(epf, epf_test->reg[bar], bar,
PRIMARY_INTERFACE);
dev_err(dev, "Failed to set BAR%d\n", bar);
if (bar == test_reg_bar)
return ret;
@ -771,7 +773,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
}
base = pci_epf_alloc_space(epf, test_reg_size, test_reg_bar,
epc_features->align);
epc_features->align, PRIMARY_INTERFACE);
if (!base) {
dev_err(dev, "Failed to allocated register space\n");
return -ENOMEM;
@ -789,7 +791,8 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
continue;
base = pci_epf_alloc_space(epf, bar_size[bar], bar,
epc_features->align);
epc_features->align,
PRIMARY_INTERFACE);
if (!base)
dev_err(dev, "Failed to allocate space for BAR%d\n",
bar);
@ -834,6 +837,8 @@ static int pci_epf_test_bind(struct pci_epf *epf)
linkup_notifier = epc_features->linkup_notifier;
core_init_notifier = epc_features->core_init_notifier;
test_reg_bar = pci_epc_get_first_free_bar(epc_features);
if (test_reg_bar < 0)
return -EINVAL;
pci_epf_configure_bar(epf, epc_features);
}

View File

@ -21,6 +21,9 @@ static struct config_group *controllers_group;
struct pci_epf_group {
struct config_group group;
struct config_group primary_epc_group;
struct config_group secondary_epc_group;
struct delayed_work cfs_work;
struct pci_epf *epf;
int index;
};
@ -41,6 +44,127 @@ static inline struct pci_epc_group *to_pci_epc_group(struct config_item *item)
return container_of(to_config_group(item), struct pci_epc_group, group);
}
static int pci_secondary_epc_epf_link(struct config_item *epf_item,
struct config_item *epc_item)
{
int ret;
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc = epc_group->epc;
struct pci_epf *epf = epf_group->epf;
ret = pci_epc_add_epf(epc, epf, SECONDARY_INTERFACE);
if (ret)
return ret;
ret = pci_epf_bind(epf);
if (ret) {
pci_epc_remove_epf(epc, epf, SECONDARY_INTERFACE);
return ret;
}
return 0;
}
static void pci_secondary_epc_epf_unlink(struct config_item *epc_item,
struct config_item *epf_item)
{
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc;
struct pci_epf *epf;
WARN_ON_ONCE(epc_group->start);
epc = epc_group->epc;
epf = epf_group->epf;
pci_epf_unbind(epf);
pci_epc_remove_epf(epc, epf, SECONDARY_INTERFACE);
}
static struct configfs_item_operations pci_secondary_epc_item_ops = {
.allow_link = pci_secondary_epc_epf_link,
.drop_link = pci_secondary_epc_epf_unlink,
};
static const struct config_item_type pci_secondary_epc_type = {
.ct_item_ops = &pci_secondary_epc_item_ops,
.ct_owner = THIS_MODULE,
};
static struct config_group
*pci_ep_cfs_add_secondary_group(struct pci_epf_group *epf_group)
{
struct config_group *secondary_epc_group;
secondary_epc_group = &epf_group->secondary_epc_group;
config_group_init_type_name(secondary_epc_group, "secondary",
&pci_secondary_epc_type);
configfs_register_group(&epf_group->group, secondary_epc_group);
return secondary_epc_group;
}
static int pci_primary_epc_epf_link(struct config_item *epf_item,
struct config_item *epc_item)
{
int ret;
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc = epc_group->epc;
struct pci_epf *epf = epf_group->epf;
ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE);
if (ret)
return ret;
ret = pci_epf_bind(epf);
if (ret) {
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
return ret;
}
return 0;
}
static void pci_primary_epc_epf_unlink(struct config_item *epc_item,
struct config_item *epf_item)
{
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
struct pci_epc *epc;
struct pci_epf *epf;
WARN_ON_ONCE(epc_group->start);
epc = epc_group->epc;
epf = epf_group->epf;
pci_epf_unbind(epf);
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
}
static struct configfs_item_operations pci_primary_epc_item_ops = {
.allow_link = pci_primary_epc_epf_link,
.drop_link = pci_primary_epc_epf_unlink,
};
static const struct config_item_type pci_primary_epc_type = {
.ct_item_ops = &pci_primary_epc_item_ops,
.ct_owner = THIS_MODULE,
};
static struct config_group
*pci_ep_cfs_add_primary_group(struct pci_epf_group *epf_group)
{
struct config_group *primary_epc_group = &epf_group->primary_epc_group;
config_group_init_type_name(primary_epc_group, "primary",
&pci_primary_epc_type);
configfs_register_group(&epf_group->group, primary_epc_group);
return primary_epc_group;
}
static ssize_t pci_epc_start_store(struct config_item *item, const char *page,
size_t len)
{
@ -94,13 +218,13 @@ static int pci_epc_epf_link(struct config_item *epc_item,
struct pci_epc *epc = epc_group->epc;
struct pci_epf *epf = epf_group->epf;
ret = pci_epc_add_epf(epc, epf);
ret = pci_epc_add_epf(epc, epf, PRIMARY_INTERFACE);
if (ret)
return ret;
ret = pci_epf_bind(epf);
if (ret) {
pci_epc_remove_epf(epc, epf);
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
return ret;
}
@ -120,7 +244,7 @@ static void pci_epc_epf_unlink(struct config_item *epc_item,
epc = epc_group->epc;
epf = epf_group->epf;
pci_epf_unbind(epf);
pci_epc_remove_epf(epc, epf);
pci_epc_remove_epf(epc, epf, PRIMARY_INTERFACE);
}
static struct configfs_item_operations pci_epc_item_ops = {
@ -366,12 +490,53 @@ static struct configfs_item_operations pci_epf_ops = {
.release = pci_epf_release,
};
static struct config_group *pci_epf_type_make(struct config_group *group,
const char *name)
{
struct pci_epf_group *epf_group = to_pci_epf_group(&group->cg_item);
struct config_group *epf_type_group;
epf_type_group = pci_epf_type_add_cfs(epf_group->epf, group);
return epf_type_group;
}
static void pci_epf_type_drop(struct config_group *group,
struct config_item *item)
{
config_item_put(item);
}
static struct configfs_group_operations pci_epf_type_group_ops = {
.make_group = &pci_epf_type_make,
.drop_item = &pci_epf_type_drop,
};
static const struct config_item_type pci_epf_type = {
.ct_group_ops = &pci_epf_type_group_ops,
.ct_item_ops = &pci_epf_ops,
.ct_attrs = pci_epf_attrs,
.ct_owner = THIS_MODULE,
};
static void pci_epf_cfs_work(struct work_struct *work)
{
struct pci_epf_group *epf_group;
struct config_group *group;
epf_group = container_of(work, struct pci_epf_group, cfs_work.work);
group = pci_ep_cfs_add_primary_group(epf_group);
if (IS_ERR(group)) {
pr_err("failed to create 'primary' EPC interface\n");
return;
}
group = pci_ep_cfs_add_secondary_group(epf_group);
if (IS_ERR(group)) {
pr_err("failed to create 'secondary' EPC interface\n");
return;
}
}
static struct config_group *pci_epf_make(struct config_group *group,
const char *name)
{
@ -410,10 +575,15 @@ static struct config_group *pci_epf_make(struct config_group *group,
goto free_name;
}
epf->group = &epf_group->group;
epf_group->epf = epf;
kfree(epf_name);
INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work);
queue_delayed_work(system_wq, &epf_group->cfs_work,
msecs_to_jiffies(1));
return &epf_group->group;
free_name:

View File

@ -87,24 +87,50 @@ EXPORT_SYMBOL_GPL(pci_epc_get);
* pci_epc_get_first_free_bar() - helper to get first unreserved BAR
* @epc_features: pci_epc_features structure that holds the reserved bar bitmap
*
* Invoke to get the first unreserved BAR that can be used for endpoint
* Invoke to get the first unreserved BAR that can be used by the endpoint
* function. For any incorrect value in reserved_bar return '0'.
*/
unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features
*epc_features)
enum pci_barno
pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features)
{
int free_bar;
return pci_epc_get_next_free_bar(epc_features, BAR_0);
}
EXPORT_SYMBOL_GPL(pci_epc_get_first_free_bar);
/**
* pci_epc_get_next_free_bar() - helper to get unreserved BAR starting from @bar
* @epc_features: pci_epc_features structure that holds the reserved bar bitmap
* @bar: the starting BAR number from where unreserved BAR should be searched
*
* Invoke to get the next unreserved BAR starting from @bar that can be used
* for endpoint function. For any incorrect value in reserved_bar return '0'.
*/
enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
*epc_features, enum pci_barno bar)
{
unsigned long free_bar;
if (!epc_features)
return 0;
return BAR_0;
free_bar = ffz(epc_features->reserved_bar);
/* If 'bar - 1' is a 64-bit BAR, move to the next BAR */
if ((epc_features->bar_fixed_64bit << 1) & 1 << bar)
bar++;
/* Find if the reserved BAR is also a 64-bit BAR */
free_bar = epc_features->reserved_bar & epc_features->bar_fixed_64bit;
/* Set the adjacent bit if the reserved BAR is also a 64-bit BAR */
free_bar <<= 1;
free_bar |= epc_features->reserved_bar;
free_bar = find_next_zero_bit(&free_bar, 6, bar);
if (free_bar > 5)
return 0;
return NO_BAR;
return free_bar;
}
EXPORT_SYMBOL_GPL(pci_epc_get_first_free_bar);
EXPORT_SYMBOL_GPL(pci_epc_get_next_free_bar);
/**
* pci_epc_get_features() - get the features supported by EPC
@ -204,6 +230,47 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
}
EXPORT_SYMBOL_GPL(pci_epc_raise_irq);
/**
* pci_epc_map_msi_irq() - Map physical address to MSI address and return
* MSI data
* @epc: the EPC device which has the MSI capability
* @func_no: the physical endpoint function number in the EPC device
* @phys_addr: the physical address of the outbound region
* @interrupt_num: the MSI interrupt number
* @entry_size: Size of Outbound address region for each interrupt
* @msi_data: the data that should be written in order to raise MSI interrupt
* with interrupt number as 'interrupt num'
* @msi_addr_offset: Offset of MSI address from the aligned outbound address
* to which the MSI address is mapped
*
* Invoke to map physical address to MSI address and return MSI data. The
* physical address should be an address in the outbound region. This is
* required to implement doorbell functionality of NTB wherein EPC on either
* side of the interface (primary and secondary) can directly write to the
* physical address (in outbound region) of the other interface to ring
* doorbell.
*/
int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr,
u8 interrupt_num, u32 entry_size, u32 *msi_data,
u32 *msi_addr_offset)
{
int ret;
if (IS_ERR_OR_NULL(epc))
return -EINVAL;
if (!epc->ops->map_msi_irq)
return -EINVAL;
mutex_lock(&epc->lock);
ret = epc->ops->map_msi_irq(epc, func_no, phys_addr, interrupt_num,
entry_size, msi_data, msi_addr_offset);
mutex_unlock(&epc->lock);
return ret;
}
EXPORT_SYMBOL_GPL(pci_epc_map_msi_irq);
/**
* pci_epc_get_msi() - get the number of MSI interrupt numbers allocated
* @epc: the EPC device to which MSI interrupts was requested
@ -467,21 +534,28 @@ EXPORT_SYMBOL_GPL(pci_epc_write_header);
* pci_epc_add_epf() - bind PCI endpoint function to an endpoint controller
* @epc: the EPC device to which the endpoint function should be added
* @epf: the endpoint function to be added
* @type: Identifies if the EPC is connected to the primary or secondary
* interface of EPF
*
* A PCI endpoint device can have one or more functions. In the case of PCIe,
* the specification allows up to 8 PCIe endpoint functions. Invoke
* pci_epc_add_epf() to add a PCI endpoint function to an endpoint controller.
*/
int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf)
int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf,
enum pci_epc_interface_type type)
{
struct list_head *list;
u32 func_no;
int ret = 0;
if (epf->epc)
if (IS_ERR_OR_NULL(epc))
return -EINVAL;
if (type == PRIMARY_INTERFACE && epf->epc)
return -EBUSY;
if (IS_ERR(epc))
return -EINVAL;
if (type == SECONDARY_INTERFACE && epf->sec_epc)
return -EBUSY;
mutex_lock(&epc->lock);
func_no = find_first_zero_bit(&epc->function_num_map,
@ -498,11 +572,17 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf)
}
set_bit(func_no, &epc->function_num_map);
epf->func_no = func_no;
epf->epc = epc;
list_add_tail(&epf->list, &epc->pci_epf);
if (type == PRIMARY_INTERFACE) {
epf->func_no = func_no;
epf->epc = epc;
list = &epf->list;
} else {
epf->sec_epc_func_no = func_no;
epf->sec_epc = epc;
list = &epf->sec_epc_list;
}
list_add_tail(list, &epc->pci_epf);
ret:
mutex_unlock(&epc->lock);
@ -517,14 +597,26 @@ EXPORT_SYMBOL_GPL(pci_epc_add_epf);
*
* Invoke to remove PCI endpoint function from the endpoint controller.
*/
void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf)
void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
enum pci_epc_interface_type type)
{
struct list_head *list;
u32 func_no = 0;
if (!epc || IS_ERR(epc) || !epf)
return;
if (type == PRIMARY_INTERFACE) {
func_no = epf->func_no;
list = &epf->list;
} else {
func_no = epf->sec_epc_func_no;
list = &epf->sec_epc_list;
}
mutex_lock(&epc->lock);
clear_bit(epf->func_no, &epc->function_num_map);
list_del(&epf->list);
clear_bit(func_no, &epc->function_num_map);
list_del(list);
epf->epc = NULL;
mutex_unlock(&epc->lock);
}

View File

@ -20,6 +20,38 @@ static DEFINE_MUTEX(pci_epf_mutex);
static struct bus_type pci_epf_bus_type;
static const struct device_type pci_epf_type;
/**
* pci_epf_type_add_cfs() - Help function drivers to expose function specific
* attributes in configfs
* @epf: the EPF device that has to be configured using configfs
* @group: the parent configfs group (corresponding to entries in
* pci_epf_device_id)
*
* Invoke to expose function specific attributes in configfs. If the function
* driver does not have anything to expose (attributes configured by user),
* return NULL.
*/
struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf,
struct config_group *group)
{
struct config_group *epf_type_group;
if (!epf->driver) {
dev_err(&epf->dev, "epf device not bound to driver\n");
return NULL;
}
if (!epf->driver->ops->add_cfs)
return NULL;
mutex_lock(&epf->lock);
epf_type_group = epf->driver->ops->add_cfs(epf, group);
mutex_unlock(&epf->lock);
return epf_type_group;
}
EXPORT_SYMBOL_GPL(pci_epf_type_add_cfs);
/**
* pci_epf_unbind() - Notify the function driver that the binding between the
* EPF device and EPC device has been lost
@ -74,24 +106,37 @@ EXPORT_SYMBOL_GPL(pci_epf_bind);
* @epf: the EPF device from whom to free the memory
* @addr: the virtual address of the PCI EPF register space
* @bar: the BAR number corresponding to the register space
* @type: Identifies if the allocated space is for primary EPC or secondary EPC
*
* Invoke to free the allocated PCI EPF register space.
*/
void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar)
void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar,
enum pci_epc_interface_type type)
{
struct device *dev = epf->epc->dev.parent;
struct pci_epf_bar *epf_bar;
struct pci_epc *epc;
if (!addr)
return;
dma_free_coherent(dev, epf->bar[bar].size, addr,
epf->bar[bar].phys_addr);
if (type == PRIMARY_INTERFACE) {
epc = epf->epc;
epf_bar = epf->bar;
} else {
epc = epf->sec_epc;
epf_bar = epf->sec_epc_bar;
}
epf->bar[bar].phys_addr = 0;
epf->bar[bar].addr = NULL;
epf->bar[bar].size = 0;
epf->bar[bar].barno = 0;
epf->bar[bar].flags = 0;
dev = epc->dev.parent;
dma_free_coherent(dev, epf_bar[bar].size, addr,
epf_bar[bar].phys_addr);
epf_bar[bar].phys_addr = 0;
epf_bar[bar].addr = NULL;
epf_bar[bar].size = 0;
epf_bar[bar].barno = 0;
epf_bar[bar].flags = 0;
}
EXPORT_SYMBOL_GPL(pci_epf_free_space);
@ -101,15 +146,18 @@ EXPORT_SYMBOL_GPL(pci_epf_free_space);
* @size: the size of the memory that has to be allocated
* @bar: the BAR number corresponding to the allocated register space
* @align: alignment size for the allocation region
* @type: Identifies if the allocation is for primary EPC or secondary EPC
*
* Invoke to allocate memory for the PCI EPF register space.
*/
void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
size_t align)
size_t align, enum pci_epc_interface_type type)
{
void *space;
struct device *dev = epf->epc->dev.parent;
struct pci_epf_bar *epf_bar;
dma_addr_t phys_addr;
struct pci_epc *epc;
struct device *dev;
void *space;
if (size < 128)
size = 128;
@ -119,17 +167,26 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
else
size = roundup_pow_of_two(size);
if (type == PRIMARY_INTERFACE) {
epc = epf->epc;
epf_bar = epf->bar;
} else {
epc = epf->sec_epc;
epf_bar = epf->sec_epc_bar;
}
dev = epc->dev.parent;
space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
if (!space) {
dev_err(dev, "failed to allocate mem space\n");
return NULL;
}
epf->bar[bar].phys_addr = phys_addr;
epf->bar[bar].addr = space;
epf->bar[bar].size = size;
epf->bar[bar].barno = bar;
epf->bar[bar].flags |= upper_32_bits(size) ?
epf_bar[bar].phys_addr = phys_addr;
epf_bar[bar].addr = space;
epf_bar[bar].size = size;
epf_bar[bar].barno = bar;
epf_bar[bar].flags |= upper_32_bits(size) ?
PCI_BASE_ADDRESS_MEM_TYPE_64 :
PCI_BASE_ADDRESS_MEM_TYPE_32;
@ -282,22 +339,6 @@ struct pci_epf *pci_epf_create(const char *name)
}
EXPORT_SYMBOL_GPL(pci_epf_create);
const struct pci_epf_device_id *
pci_epf_match_device(const struct pci_epf_device_id *id, struct pci_epf *epf)
{
if (!id || !epf)
return NULL;
while (*id->name) {
if (strcmp(epf->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
EXPORT_SYMBOL_GPL(pci_epf_match_device);
static void pci_epf_dev_release(struct device *dev)
{
struct pci_epf *epf = to_pci_epf(dev);

View File

@ -176,9 +176,6 @@ int acpiphp_unregister_attention(struct acpiphp_attention_info *info);
int acpiphp_register_hotplug_slot(struct acpiphp_slot *slot, unsigned int sun);
void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *slot);
/* acpiphp_glue.c */
typedef int (*acpiphp_callback)(struct acpiphp_slot *slot, void *data);
int acpiphp_enable_slot(struct acpiphp_slot *slot);
int acpiphp_disable_slot(struct acpiphp_slot *slot);
u8 acpiphp_get_power_status(struct acpiphp_slot *slot);

View File

@ -21,8 +21,9 @@
#include "pci-bridge-emul.h"
#define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF
#define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2)
#define PCI_CAP_PCIE_START PCI_BRIDGE_CONF_END
#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_EXP_SLTSTA2 + 2)
#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF)
/**
* struct pci_bridge_reg_behavior - register bits behaviors
@ -46,7 +47,8 @@ struct pci_bridge_reg_behavior {
u32 w1c;
};
static const struct pci_bridge_reg_behavior pci_regs_behavior[] = {
static const
struct pci_bridge_reg_behavior pci_regs_behavior[PCI_STD_HEADER_SIZEOF / 4] = {
[PCI_VENDOR_ID / 4] = { .ro = ~0 },
[PCI_COMMAND / 4] = {
.rw = (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
@ -164,7 +166,8 @@ static const struct pci_bridge_reg_behavior pci_regs_behavior[] = {
},
};
static const struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
static const
struct pci_bridge_reg_behavior pcie_cap_regs_behavior[PCI_CAP_PCIE_SIZEOF / 4] = {
[PCI_CAP_LIST_ID / 4] = {
/*
* Capability ID, Next Capability Pointer and
@ -260,6 +263,8 @@ static const struct pci_bridge_reg_behavior pcie_cap_regs_behavior[] = {
int pci_bridge_emul_init(struct pci_bridge_emul *bridge,
unsigned int flags)
{
BUILD_BUG_ON(sizeof(bridge->conf) != PCI_BRIDGE_CONF_END);
bridge->conf.class_revision |= cpu_to_le32(PCI_CLASS_BRIDGE_PCI << 16);
bridge->conf.header_type = PCI_HEADER_TYPE_BRIDGE;
bridge->conf.cache_line_size = 0x10;

View File

@ -4030,6 +4030,10 @@ int pci_register_io_range(struct fwnode_handle *fwnode, phys_addr_t addr,
ret = logic_pio_register_range(range);
if (ret)
kfree(range);
/* Ignore duplicates due to deferred probing */
if (ret == -EEXIST)
ret = 0;
#endif
return ret;

View File

@ -133,14 +133,6 @@ config PCIE_PTM
This is only useful if you have devices that support PTM, but it
is safe to enable even if you don't.
config PCIE_BW
bool "PCI Express Bandwidth Change Notification"
depends on PCIEPORTBUS
help
This enables PCI Express Bandwidth Change Notification. If
you know link width or rate changes occur only to correct
unreliable links, you may answer Y.
config PCIE_EDR
bool "PCI Express Error Disconnect Recover support"
depends on PCIE_DPC && ACPI

View File

@ -12,5 +12,4 @@ obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o
obj-$(CONFIG_PCIE_PME) += pme.o
obj-$(CONFIG_PCIE_DPC) += dpc.o
obj-$(CONFIG_PCIE_PTM) += ptm.o
obj-$(CONFIG_PCIE_BW) += bw_notification.o
obj-$(CONFIG_PCIE_EDR) += edr.o

View File

@ -1388,7 +1388,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
if (type == PCI_EXP_TYPE_RC_END)
root = dev->rcec;
else
root = dev;
root = pcie_find_root_port(dev);
/*
* If the platform retained control of AER, an RCiEP may not have
@ -1414,7 +1414,8 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
}
} else {
rc = pci_bus_error_reset(dev);
pci_info(dev, "Root Port link has been reset (%d)\n", rc);
pci_info(dev, "%s Port link has been reset (%d)\n",
pci_is_root_bus(dev->bus) ? "Root" : "Downstream", rc);
}
if ((host->native_aer || pcie_ports_native) && aer) {

View File

@ -1,138 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* PCI Express Link Bandwidth Notification services driver
* Author: Alexandru Gagniuc <mr.nuke.me@gmail.com>
*
* Copyright (C) 2019, Dell Inc
*
* The PCIe Link Bandwidth Notification provides a way to notify the
* operating system when the link width or data rate changes. This
* capability is required for all root ports and downstream ports
* supporting links wider than x1 and/or multiple link speeds.
*
* This service port driver hooks into the bandwidth notification interrupt
* and warns when links become degraded in operation.
*/
#define dev_fmt(fmt) "bw_notification: " fmt
#include "../pci.h"
#include "portdrv.h"
static bool pcie_link_bandwidth_notification_supported(struct pci_dev *dev)
{
int ret;
u32 lnk_cap;
ret = pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnk_cap);
return (ret == PCIBIOS_SUCCESSFUL) && (lnk_cap & PCI_EXP_LNKCAP_LBNC);
}
static void pcie_enable_link_bandwidth_notification(struct pci_dev *dev)
{
u16 lnk_ctl;
pcie_capability_write_word(dev, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_LBMS);
pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
lnk_ctl |= PCI_EXP_LNKCTL_LBMIE;
pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
}
static void pcie_disable_link_bandwidth_notification(struct pci_dev *dev)
{
u16 lnk_ctl;
pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &lnk_ctl);
lnk_ctl &= ~PCI_EXP_LNKCTL_LBMIE;
pcie_capability_write_word(dev, PCI_EXP_LNKCTL, lnk_ctl);
}
static irqreturn_t pcie_bw_notification_irq(int irq, void *context)
{
struct pcie_device *srv = context;
struct pci_dev *port = srv->port;
u16 link_status, events;
int ret;
ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status);
events = link_status & PCI_EXP_LNKSTA_LBMS;
if (ret != PCIBIOS_SUCCESSFUL || !events)
return IRQ_NONE;
pcie_capability_write_word(port, PCI_EXP_LNKSTA, events);
pcie_update_link_speed(port->subordinate, link_status);
return IRQ_WAKE_THREAD;
}
static irqreturn_t pcie_bw_notification_handler(int irq, void *context)
{
struct pcie_device *srv = context;
struct pci_dev *port = srv->port;
struct pci_dev *dev;
/*
* Print status from downstream devices, not this root port or
* downstream switch port.
*/
down_read(&pci_bus_sem);
list_for_each_entry(dev, &port->subordinate->devices, bus_list)
pcie_report_downtraining(dev);
up_read(&pci_bus_sem);
return IRQ_HANDLED;
}
static int pcie_bandwidth_notification_probe(struct pcie_device *srv)
{
int ret;
/* Single-width or single-speed ports do not have to support this. */
if (!pcie_link_bandwidth_notification_supported(srv->port))
return -ENODEV;
ret = request_threaded_irq(srv->irq, pcie_bw_notification_irq,
pcie_bw_notification_handler,
IRQF_SHARED, "PCIe BW notif", srv);
if (ret)
return ret;
pcie_enable_link_bandwidth_notification(srv->port);
pci_info(srv->port, "enabled with IRQ %d\n", srv->irq);
return 0;
}
static void pcie_bandwidth_notification_remove(struct pcie_device *srv)
{
pcie_disable_link_bandwidth_notification(srv->port);
free_irq(srv->irq, srv);
}
static int pcie_bandwidth_notification_suspend(struct pcie_device *srv)
{
pcie_disable_link_bandwidth_notification(srv->port);
return 0;
}
static int pcie_bandwidth_notification_resume(struct pcie_device *srv)
{
pcie_enable_link_bandwidth_notification(srv->port);
return 0;
}
static struct pcie_port_service_driver pcie_bandwidth_notification_driver = {
.name = "pcie_bw_notification",
.port_type = PCIE_ANY_PORT,
.service = PCIE_PORT_SERVICE_BWNOTIF,
.probe = pcie_bandwidth_notification_probe,
.suspend = pcie_bandwidth_notification_suspend,
.resume = pcie_bandwidth_notification_resume,
.remove = pcie_bandwidth_notification_remove,
};
int __init pcie_bandwidth_notification_init(void)
{
return pcie_port_service_register(&pcie_bandwidth_notification_driver);
}

View File

@ -198,8 +198,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
pci_dbg(bridge, "broadcast error_detected message\n");
if (state == pci_channel_io_frozen) {
pci_walk_bridge(bridge, report_frozen_detected, &status);
status = reset_subordinates(bridge);
if (status != PCI_ERS_RESULT_RECOVERED) {
if (reset_subordinates(bridge) != PCI_ERS_RESULT_RECOVERED) {
pci_warn(bridge, "subordinate device reset failed\n");
goto failed;
}
@ -231,15 +230,14 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
pci_walk_bridge(bridge, report_resume, &status);
/*
* If we have native control of AER, clear error status in the Root
* Port or Downstream Port that signaled the error. If the
* platform retained control of AER, it is responsible for clearing
* this status. In that case, the signaling device may not even be
* visible to the OS.
* If we have native control of AER, clear error status in the device
* that detected the error. If the platform retained control of AER,
* 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) {
pcie_clear_device_status(bridge);
pci_aer_clear_nonfatal_status(bridge);
pcie_clear_device_status(dev);
pci_aer_clear_nonfatal_status(dev);
}
pci_info(bridge, "device recovery successful\n");
return status;

View File

@ -53,12 +53,6 @@ int pcie_dpc_init(void);
static inline int pcie_dpc_init(void) { return 0; }
#endif
#ifdef CONFIG_PCIE_BW
int pcie_bandwidth_notification_init(void);
#else
static inline int pcie_bandwidth_notification_init(void) { return 0; }
#endif
/* Port Type */
#define PCIE_ANY_PORT (~0)

View File

@ -153,7 +153,8 @@ static void pcie_portdrv_remove(struct pci_dev *dev)
static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
pci_channel_state_t error)
{
/* Root Port has no impact. Always recovers. */
if (error == pci_channel_io_frozen)
return PCI_ERS_RESULT_NEED_RESET;
return PCI_ERS_RESULT_CAN_RECOVER;
}
@ -255,7 +256,6 @@ static void __init pcie_init_services(void)
pcie_pme_init();
pcie_dpc_init();
pcie_hp_init();
pcie_bandwidth_notification_init();
}
static int __init pcie_portdrv_init(void)

View File

@ -168,7 +168,6 @@ struct pci_bus *pci_find_next_bus(const struct pci_bus *from)
struct list_head *n;
struct pci_bus *b = NULL;
WARN_ON(in_interrupt());
down_read(&pci_bus_sem);
n = from ? from->node.next : pci_root_buses.next;
if (n != &pci_root_buses)
@ -196,7 +195,6 @@ struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)
{
struct pci_dev *dev;
WARN_ON(in_interrupt());
down_read(&pci_bus_sem);
list_for_each_entry(dev, &bus->devices, bus_list) {
@ -274,7 +272,6 @@ static struct pci_dev *pci_get_dev_by_id(const struct pci_device_id *id,
struct device *dev_start = NULL;
struct pci_dev *pdev = NULL;
WARN_ON(in_interrupt());
if (from)
dev_start = &from->dev;
dev = bus_find_device(&pci_bus_type, dev_start, (void *)id,
@ -381,7 +378,6 @@ int pci_dev_present(const struct pci_device_id *ids)
{
struct pci_dev *found = NULL;
WARN_ON(in_interrupt());
while (ids->vendor || ids->subvendor || ids->class_mask) {
found = pci_get_dev_by_id(ids, NULL);
if (found) {

View File

@ -410,10 +410,16 @@ EXPORT_SYMBOL(pci_release_resource);
int pci_resize_resource(struct pci_dev *dev, int resno, int size)
{
struct resource *res = dev->resource + resno;
struct pci_host_bridge *host;
int old, ret;
u32 sizes;
u16 cmd;
/* Check if we must preserve the firmware's resource assignment */
host = pci_find_host_bridge(dev->bus);
if (host->preserve_config)
return -ENOTSUPP;
/* Make sure the resource isn't assigned before resizing it. */
if (!(res->flags & IORESOURCE_UNSET))
return -EBUSY;

View File

@ -20,7 +20,7 @@ SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,
u16 word;
u32 dword;
long err;
long cfg_ret;
int cfg_ret;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@ -46,7 +46,7 @@ SYSCALL_DEFINE5(pciconfig_read, unsigned long, bus, unsigned long, dfn,
}
err = -EIO;
if (cfg_ret != PCIBIOS_SUCCESSFUL)
if (cfg_ret)
goto error;
switch (len) {
@ -105,7 +105,7 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
if (err)
break;
err = pci_user_write_config_byte(dev, off, byte);
if (err != PCIBIOS_SUCCESSFUL)
if (err)
err = -EIO;
break;
@ -114,7 +114,7 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
if (err)
break;
err = pci_user_write_config_word(dev, off, word);
if (err != PCIBIOS_SUCCESSFUL)
if (err)
err = -EIO;
break;
@ -123,7 +123,7 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn,
if (err)
break;
err = pci_user_write_config_dword(dev, off, dword);
if (err != PCIBIOS_SUCCESSFUL)
if (err)
err = -EIO;
break;

View File

@ -591,9 +591,6 @@ extern u32 osc_sb_native_usb4_control;
#define ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES 0x0000000E
#define ACPI_GSB_ACCESS_ATTRIB_RAW_PROCESS 0x0000000F
extern acpi_status acpi_pci_osc_control_set(acpi_handle handle,
u32 *mask, u32 req);
/* Enable _OST when all relevant hotplug operations are enabled */
#if defined(CONFIG_ACPI_HOTPLUG_CPU) && \
defined(CONFIG_ACPI_HOTPLUG_MEMORY) && \

View File

@ -13,6 +13,12 @@
struct pci_epc;
enum pci_epc_interface_type {
UNKNOWN_INTERFACE = -1,
PRIMARY_INTERFACE,
SECONDARY_INTERFACE,
};
enum pci_epc_irq_type {
PCI_EPC_IRQ_UNKNOWN,
PCI_EPC_IRQ_LEGACY,
@ -20,6 +26,19 @@ enum pci_epc_irq_type {
PCI_EPC_IRQ_MSIX,
};
static inline const char *
pci_epc_interface_string(enum pci_epc_interface_type type)
{
switch (type) {
case PRIMARY_INTERFACE:
return "primary";
case SECONDARY_INTERFACE:
return "secondary";
default:
return "UNKNOWN interface";
}
}
/**
* struct pci_epc_ops - set of function pointers for performing EPC operations
* @write_header: ops to populate configuration space header
@ -36,6 +55,7 @@ enum pci_epc_irq_type {
* @get_msix: ops to get the number of MSI-X interrupts allocated by the RC
* from the MSI-X capability register
* @raise_irq: ops to raise a legacy, MSI or MSI-X interrupt
* @map_msi_irq: ops to map physical address to MSI address and return MSI data
* @start: ops to start the PCI link
* @stop: ops to stop the PCI link
* @owner: the module owner containing the ops
@ -58,6 +78,10 @@ struct pci_epc_ops {
int (*get_msix)(struct pci_epc *epc, u8 func_no);
int (*raise_irq)(struct pci_epc *epc, u8 func_no,
enum pci_epc_irq_type type, u16 interrupt_num);
int (*map_msi_irq)(struct pci_epc *epc, u8 func_no,
phys_addr_t phys_addr, u8 interrupt_num,
u32 entry_size, u32 *msi_data,
u32 *msi_addr_offset);
int (*start)(struct pci_epc *epc);
void (*stop)(struct pci_epc *epc);
const struct pci_epc_features* (*get_features)(struct pci_epc *epc,
@ -175,10 +199,12 @@ __pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
struct module *owner);
void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc);
void pci_epc_destroy(struct pci_epc *epc);
int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf);
int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf,
enum pci_epc_interface_type type);
void pci_epc_linkup(struct pci_epc *epc);
void pci_epc_init_notify(struct pci_epc *epc);
void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf);
void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
enum pci_epc_interface_type type);
int pci_epc_write_header(struct pci_epc *epc, u8 func_no,
struct pci_epf_header *hdr);
int pci_epc_set_bar(struct pci_epc *epc, u8 func_no,
@ -195,14 +221,19 @@ int pci_epc_get_msi(struct pci_epc *epc, u8 func_no);
int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts,
enum pci_barno, u32 offset);
int pci_epc_get_msix(struct pci_epc *epc, u8 func_no);
int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no,
phys_addr_t phys_addr, u8 interrupt_num,
u32 entry_size, u32 *msi_data, u32 *msi_addr_offset);
int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
enum pci_epc_irq_type type, u16 interrupt_num);
int pci_epc_start(struct pci_epc *epc);
void pci_epc_stop(struct pci_epc *epc);
const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
u8 func_no);
unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features
*epc_features);
enum pci_barno
pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features);
enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
*epc_features, enum pci_barno bar);
struct pci_epc *pci_epc_get(const char *epc_name);
void pci_epc_put(struct pci_epc *epc);

View File

@ -9,11 +9,13 @@
#ifndef __LINUX_PCI_EPF_H
#define __LINUX_PCI_EPF_H
#include <linux/configfs.h>
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/pci.h>
struct pci_epf;
enum pci_epc_interface_type;
enum pci_notify_event {
CORE_INIT,
@ -21,6 +23,7 @@ enum pci_notify_event {
};
enum pci_barno {
NO_BAR = -1,
BAR_0,
BAR_1,
BAR_2,
@ -60,10 +63,13 @@ struct pci_epf_header {
* @bind: ops to perform when a EPC device has been bound to EPF device
* @unbind: ops to perform when a binding has been lost between a EPC device
* and EPF device
* @add_cfs: ops to initialize function specific configfs attributes
*/
struct pci_epf_ops {
int (*bind)(struct pci_epf *epf);
void (*unbind)(struct pci_epf *epf);
struct config_group *(*add_cfs)(struct pci_epf *epf,
struct config_group *group);
};
/**
@ -118,6 +124,12 @@ struct pci_epf_bar {
* @list: to add pci_epf as a list of PCI endpoint functions to pci_epc
* @nb: notifier block to notify EPF of any EPC events (like linkup)
* @lock: mutex to protect pci_epf_ops
* @sec_epc: the secondary EPC device to which this EPF device is bound
* @sec_epc_list: to add pci_epf as list of PCI endpoint functions to secondary
* EPC device
* @sec_epc_bar: represents the BAR of EPF device associated with secondary EPC
* @sec_epc_func_no: unique (physical) function number within the secondary EPC
* @group: configfs group associated with the EPF device
*/
struct pci_epf {
struct device dev;
@ -134,6 +146,13 @@ struct pci_epf {
struct notifier_block nb;
/* mutex to protect against concurrent access of pci_epf_ops */
struct mutex lock;
/* Below members are to attach secondary EPC to an endpoint function */
struct pci_epc *sec_epc;
struct list_head sec_epc_list;
struct pci_epf_bar sec_epc_bar[6];
u8 sec_epc_func_no;
struct config_group *group;
};
/**
@ -164,16 +183,17 @@ static inline void *epf_get_drvdata(struct pci_epf *epf)
return dev_get_drvdata(&epf->dev);
}
const struct pci_epf_device_id *
pci_epf_match_device(const struct pci_epf_device_id *id, struct pci_epf *epf);
struct pci_epf *pci_epf_create(const char *name);
void pci_epf_destroy(struct pci_epf *epf);
int __pci_epf_register_driver(struct pci_epf_driver *driver,
struct module *owner);
void pci_epf_unregister_driver(struct pci_epf_driver *driver);
void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
size_t align);
void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar);
size_t align, enum pci_epc_interface_type type);
void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar,
enum pci_epc_interface_type type);
int pci_epf_bind(struct pci_epf *epf);
void pci_epf_unbind(struct pci_epf *epf);
struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf,
struct config_group *group);
#endif /* __LINUX_PCI_EPF_H */

View File

@ -882,6 +882,7 @@
#define PCI_DEVICE_ID_TI_X620 0xac8d
#define PCI_DEVICE_ID_TI_X420 0xac8e
#define PCI_DEVICE_ID_TI_XX20_FM 0xac8f
#define PCI_DEVICE_ID_TI_J721E 0xb00d
#define PCI_DEVICE_ID_TI_DRA74x 0xb500
#define PCI_DEVICE_ID_TI_DRA72x 0xb501
@ -2589,6 +2590,8 @@
#define PCI_VENDOR_ID_REDHAT 0x1b36
#define PCI_VENDOR_ID_SILICOM_DENMARK 0x1c2c
#define PCI_VENDOR_ID_AMAZON_ANNAPURNA_LABS 0x1c36
#define PCI_VENDOR_ID_CIRCUITCO 0x1cc8

View File

@ -28,6 +28,8 @@ static DEFINE_MUTEX(io_range_mutex);
* @new_range: pointer to the IO range to be registered.
*
* Returns 0 on success, the error code in case of failure.
* If the range already exists, -EEXIST will be returned, which should be
* considered a success.
*
* Register a new IO range node in the IO range list.
*/
@ -51,6 +53,7 @@ int logic_pio_register_range(struct logic_pio_hwaddr *new_range)
list_for_each_entry(range, &io_range_list, list) {
if (range->fwnode == new_range->fwnode) {
/* range already there */
ret = -EEXIST;
goto end_register;
}
if (range->flags == LOGIC_PIO_CPU_MMIO &&