mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
Merge branch 'pci/ntb'
- Account for 64-bit BARs in pci_epc_get_first_free_bar() (Kishon Vijay Abraham I) - Add pci_epc_get_next_free_bar() helper (Kishon Vijay Abraham I) - Return error codes on failure of endpoint BAR interfaces (Kishon Vijay Abraham I) - Remove unused pci_epf_match_device() (Kishon Vijay Abraham I) - Add support for secondary endpoint controller to prepare for NTB endpoint functionality (Kishon Vijay Abraham I) - Add configfs support for secondary endpoint controller (Kishon Vijay Abraham I) - Add MSI address mapping ops for NTB doorbell support (Kishon Vijay Abraham I) - Add ops for endpoint function-specific attributes (Kishon Vijay Abraham I) - Allow configfs subdirectory for endpoint function configuration (Kishon Vijay Abraham I) - Implement cadence MSI address mapping ops (Kishon Vijay Abraham I) - Configure cadence LM_EP_FUNC_CFG based on epc->function_num_map (Kishon Vijay Abraham I) - Add endpoint-side driver to provide NTB functionality (Kishon Vijay Abraham I) - Add host-side driver for generic EPF NTB functionality (Kishon Vijay Abraham I) - Document NTB endpoint functionality (Kishon Vijay Abraham I) * pci/ntb: Documentation: PCI: Add PCI endpoint NTB function user guide Documentation: PCI: Add configfs binding documentation for pci-ntb endpoint function NTB: Add support for EPF PCI Non-Transparent Bridge PCI: Add TI J721E device to PCI IDs PCI: endpoint: Add EP function driver to provide NTB functionality PCI: cadence: Configure LM_EP_FUNC_CFG based on epc->function_num_map PCI: cadence: Implement ->msi_map_irq() ops PCI: endpoint: Allow user to create sub-directory of 'EPF Device' directory PCI: endpoint: Add pci_epf_ops to expose function-specific attrs PCI: endpoint: Add pci_epc_ops to map MSI IRQ PCI: endpoint: Add support in configfs to associate two EPCs with EPF PCI: endpoint: Add support to associate secondary EPC with EPF PCI: endpoint: Remove unused pci_epf_match_device() PCI: endpoint: Make *_free_bar() to return error codes on failure PCI: endpoint: Add helper API to get the 'next' unreserved BAR PCI: endpoint: Make *_get_first_free_bar() take into account 64 bit BAR Documentation: PCI: Add specification for the PCI NTB function device
This commit is contained in:
commit
2ef38d7e2b
22 changed files with 3944 additions and 73 deletions
38
Documentation/PCI/endpoint/function/binding/pci-ntb.rst
Normal file
38
Documentation/PCI/endpoint/function/binding/pci-ntb.rst
Normal 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
|
||||
================ ===========================================================
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
==========
|
||||
|
|
348
Documentation/PCI/endpoint/pci-ntb-function.rst
Normal file
348
Documentation/PCI/endpoint/pci-ntb-function.rst
Normal 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.
|
161
Documentation/PCI/endpoint/pci-ntb-howto.rst
Normal file
161
Documentation/PCI/endpoint/pci-ntb-howto.rst
Normal 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>`
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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/
|
||||
|
|
6
drivers/ntb/hw/epf/Kconfig
Normal file
6
drivers/ntb/hw/epf/Kconfig
Normal 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.
|
1
drivers/ntb/hw/epf/Makefile
Normal file
1
drivers/ntb/hw/epf/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_NTB_EPF) += ntb_hw_epf.o
|
753
drivers/ntb/hw/epf/ntb_hw_epf.c
Normal file
753
drivers/ntb/hw/epf/ntb_hw_epf.c
Normal 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");
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o
|
||||
obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o
|
||||
|
|
2128
drivers/pci/endpoint/functions/pci-epf-ntb.c
Normal file
2128
drivers/pci/endpoint/functions/pci-epf-ntb.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -881,6 +881,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
|
||||
|
||||
|
|
Loading…
Reference in a new issue