Merge branch 'pci/host-rockchip' into next
authorBjorn Helgaas <bhelgaas@google.com>
Fri, 28 Apr 2017 15:33:10 +0000 (10:33 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 28 Apr 2017 15:33:10 +0000 (10:33 -0500)
* pci/host-rockchip:
  PCI: rockchip: Modularize
  PCI: Export pci_remap_iospace() and pci_unmap_iospace()
  PCI: rockchip: Add remove() support
  PCI: rockchip: Set PCI_EXP_LNKSTA_SLC in the Root Port
  PCI: rockchip: Advertise 128-byte Read Completion Boundary support
  PCI: rockchip: Make 'return 0' more obvious in probe()
  PCI: rockchip: Unindent rockchip_pcie_set_power_limit()
  PCI: rockchip: Handle regulator_get_current_limit() failure correctly

59 files changed:
Documentation/PCI/00-INDEX
Documentation/PCI/endpoint/function/binding/pci-test.txt [new file with mode: 0644]
Documentation/PCI/endpoint/pci-endpoint-cfs.txt [new file with mode: 0644]
Documentation/PCI/endpoint/pci-endpoint.txt [new file with mode: 0644]
Documentation/PCI/endpoint/pci-test-function.txt [new file with mode: 0644]
Documentation/PCI/endpoint/pci-test-howto.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/designware-pcie.txt
Documentation/devicetree/bindings/pci/faraday,ftpci100.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.txt
Documentation/devicetree/bindings/pci/ti-pci.txt
Documentation/misc-devices/pci-endpoint-test.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/mach-omap2/clockdomains7xx_data.c
drivers/Makefile
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/pci_endpoint_test.c [new file with mode: 0644]
drivers/pci/Kconfig
drivers/pci/dwc/Kconfig
drivers/pci/dwc/Makefile
drivers/pci/dwc/pci-dra7xx.c
drivers/pci/dwc/pci-exynos.c
drivers/pci/dwc/pci-imx6.c
drivers/pci/dwc/pcie-artpec6.c
drivers/pci/dwc/pcie-designware-ep.c [new file with mode: 0644]
drivers/pci/dwc/pcie-designware-host.c
drivers/pci/dwc/pcie-designware-plat.c
drivers/pci/dwc/pcie-designware.c
drivers/pci/dwc/pcie-designware.h
drivers/pci/endpoint/Kconfig [new file with mode: 0644]
drivers/pci/endpoint/Makefile [new file with mode: 0644]
drivers/pci/endpoint/functions/Kconfig [new file with mode: 0644]
drivers/pci/endpoint/functions/Makefile [new file with mode: 0644]
drivers/pci/endpoint/functions/pci-epf-test.c [new file with mode: 0644]
drivers/pci/endpoint/pci-ep-cfs.c [new file with mode: 0644]
drivers/pci/endpoint/pci-epc-core.c [new file with mode: 0644]
drivers/pci/endpoint/pci-epc-mem.c [new file with mode: 0644]
drivers/pci/endpoint/pci-epf-core.c [new file with mode: 0644]
drivers/pci/host/Kconfig
drivers/pci/host/Makefile
drivers/pci/host/pci-ftpci100.c [new file with mode: 0644]
drivers/pci/host/pci-hyperv.c
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pci-thunder-pem.c
drivers/pci/host/pcie-iproc-bcma.c
drivers/pci/host/pcie-iproc-platform.c
drivers/pci/host/pcie-iproc.h
drivers/pci/pcie/aspm.c
drivers/pci/quirks.c
include/linux/mfd/syscon/imx7-iomuxc-gpr.h
include/linux/mod_devicetable.h
include/linux/pci-ep-cfs.h [new file with mode: 0644]
include/linux/pci-epc.h [new file with mode: 0644]
include/linux/pci-epf.h [new file with mode: 0644]
include/linux/pci_ids.h
include/uapi/linux/Kbuild
include/uapi/linux/pcitest.h [new file with mode: 0644]
tools/pci/pcitest.c [new file with mode: 0644]
tools/pci/pcitest.sh [new file with mode: 0644]

index 147231f1613ef3a0a8df8d9addc2a754d5ac69af..00c9a90b6f3854050f1778961be557b10c25fa0d 100644 (file)
@@ -12,3 +12,13 @@ pci.txt
        - info on the PCI subsystem for device driver authors
 pcieaer-howto.txt
        - the PCI Express Advanced Error Reporting Driver Guide HOWTO
+endpoint/pci-endpoint.txt
+       - guide to add endpoint controller driver and endpoint function driver.
+endpoint/pci-endpoint-cfs.txt
+       - guide to use configfs to configure the PCI endpoint function.
+endpoint/pci-test-function.txt
+       - specification of *PCI test* function device.
+endpoint/pci-test-howto.txt
+       - userguide for PCI endpoint test function.
+endpoint/function/binding/
+       - binding documentation for PCI endpoint function
diff --git a/Documentation/PCI/endpoint/function/binding/pci-test.txt b/Documentation/PCI/endpoint/function/binding/pci-test.txt
new file mode 100644 (file)
index 0000000..3b68b95
--- /dev/null
@@ -0,0 +1,17 @@
+PCI TEST ENDPOINT FUNCTION
+
+name: Should be "pci_epf_test" to bind to the pci_epf_test driver.
+
+Configurable Fields:
+vendorid        : should be 0x104c
+deviceid        : should be 0xb500 for DRA74x and 0xb501 for DRA72x
+revid           : don't care
+progif_code     : don't care
+subclass_code   : don't care
+baseclass_code  : should be 0xff
+cache_line_size         : don't care
+subsys_vendor_id : don't care
+subsys_id       : don't care
+interrupt_pin   : Should be 1 - INTA, 2 - INTB, 3 - INTC, 4 -INTD
+msi_interrupts  : Should be 1 to 32 depending on the number of MSI interrupts
+                  to test
diff --git a/Documentation/PCI/endpoint/pci-endpoint-cfs.txt b/Documentation/PCI/endpoint/pci-endpoint-cfs.txt
new file mode 100644 (file)
index 0000000..d740f29
--- /dev/null
@@ -0,0 +1,105 @@
+                   CONFIGURING PCI ENDPOINT USING CONFIGFS
+                    Kishon Vijay Abraham I <kishon@ti.com>
+
+The PCI Endpoint Core exposes configfs entry (pci_ep) to configure the
+PCI endpoint function and to bind the endpoint function
+with the endpoint controller. (For introducing other mechanisms to
+configure the PCI Endpoint Function refer to [1]).
+
+*) Mounting configfs
+
+The PCI Endpoint Core layer creates pci_ep directory in the mounted configfs
+directory. configfs can be mounted using the following command.
+
+       mount -t configfs none /sys/kernel/config
+
+*) Directory Structure
+
+The pci_ep configfs has two directories at its root: controllers and
+functions. Every EPC device present in the system will have an entry in
+the *controllers* directory and and every EPF driver present in the system
+will have an entry in the *functions* directory.
+
+/sys/kernel/config/pci_ep/
+       .. controllers/
+       .. functions/
+
+*) Creating EPF Device
+
+Every registered EPF driver will be listed in controllers directory. The
+entries corresponding to EPF driver will be created by the EPF core.
+
+/sys/kernel/config/pci_ep/functions/
+       .. <EPF Driver1>/
+               ... <EPF Device 11>/
+               ... <EPF Device 21>/
+       .. <EPF Driver2>/
+               ... <EPF Device 12>/
+               ... <EPF Device 22>/
+
+In order to create a <EPF device> of the type probed by <EPF Driver>, the
+user has to create a directory inside <EPF DriverN>.
+
+Every <EPF device> directory consists of the following entries that can be
+used to configure the standard configuration header of the endpoint function.
+(These entries are created by the framework when any new <EPF Device> is
+created)
+
+       .. <EPF Driver1>/
+               ... <EPF Device 11>/
+                       ... vendorid
+                       ... deviceid
+                       ... revid
+                       ... progif_code
+                       ... subclass_code
+                       ... baseclass_code
+                       ... cache_line_size
+                       ... subsys_vendor_id
+                       ... subsys_id
+                       ... interrupt_pin
+
+*) EPC Device
+
+Every registered EPC device will be listed in controllers directory. The
+entries corresponding to EPC device will be created by the EPC core.
+
+/sys/kernel/config/pci_ep/controllers/
+       .. <EPC Device1>/
+               ... <Symlink EPF Device11>/
+               ... <Symlink EPF Device12>/
+               ... start
+       .. <EPC Device2>/
+               ... <Symlink EPF Device21>/
+               ... <Symlink EPF Device22>/
+               ... start
+
+The <EPC Device> directory will have a list of symbolic links to
+<EPF Device>. These symbolic links should be created by the user to
+represent the functions present in the endpoint device.
+
+The <EPC Device> directory will also have a *start* field. Once
+"1" is written to this field, the endpoint device will be ready to
+establish the link with the host. This is usually done after
+all the EPF devices are created and linked with the EPC device.
+
+
+                        | controllers/
+                               | <Directory: EPC name>/
+                                       | <Symbolic Link: Function>
+                                       | start
+                        | functions/
+                               | <Directory: EPF driver>/
+                                       | <Directory: EPF device>/
+                                               | vendorid
+                                               | deviceid
+                                               | revid
+                                               | progif_code
+                                               | subclass_code
+                                               | baseclass_code
+                                               | cache_line_size
+                                               | subsys_vendor_id
+                                               | subsys_id
+                                               | interrupt_pin
+                                               | function
+
+[1] -> Documentation/PCI/endpoint/pci-endpoint.txt
diff --git a/Documentation/PCI/endpoint/pci-endpoint.txt b/Documentation/PCI/endpoint/pci-endpoint.txt
new file mode 100644 (file)
index 0000000..9b1d668
--- /dev/null
@@ -0,0 +1,215 @@
+                           PCI ENDPOINT FRAMEWORK
+                   Kishon Vijay Abraham I <kishon@ti.com>
+
+This document is a guide to use the PCI Endpoint Framework in order to create
+endpoint controller driver, endpoint function driver, and using configfs
+interface to bind the function driver to the controller driver.
+
+1. Introduction
+
+Linux has a comprehensive PCI subsystem to support PCI controllers that
+operates in Root Complex mode. The subsystem has capability to scan PCI bus,
+assign memory resources and IRQ resources, load PCI driver (based on
+vendor ID, device ID), support other services like hot-plug, power management,
+advanced error reporting and virtual channels.
+
+However the PCI controller IP integrated in some SoCs is capable of operating
+either in Root Complex mode or Endpoint mode. PCI Endpoint Framework will
+add endpoint mode support in Linux. This will help to run Linux in an
+EP system which can have a wide variety of use cases from testing or
+validation, co-processor accelerator, etc.
+
+2. PCI Endpoint Core
+
+The PCI Endpoint Core layer comprises 3 components: the Endpoint Controller
+library, the Endpoint Function library, and the configfs layer to bind the
+endpoint function with the endpoint controller.
+
+2.1 PCI Endpoint Controller(EPC) Library
+
+The EPC library provides APIs to be used by the controller that can operate
+in endpoint mode. It also provides APIs to be used by function driver/library
+in order to implement a particular endpoint function.
+
+2.1.1 APIs for the PCI controller Driver
+
+This section lists the APIs that the PCI Endpoint core provides to be used
+by the PCI controller driver.
+
+*) devm_pci_epc_create()/pci_epc_create()
+
+   The PCI controller driver should implement the following ops:
+        * write_header: ops to populate configuration space header
+        * set_bar: ops to configure the BAR
+        * clear_bar: ops to reset the BAR
+        * alloc_addr_space: ops to allocate in PCI controller address space
+        * free_addr_space: ops to free the allocated address space
+        * raise_irq: ops to raise a legacy or MSI interrupt
+        * start: ops to start the PCI link
+        * stop: ops to stop the PCI link
+
+   The PCI controller driver can then create a new EPC device by invoking
+   devm_pci_epc_create()/pci_epc_create().
+
+*) devm_pci_epc_destroy()/pci_epc_destroy()
+
+   The PCI controller driver can destroy the EPC device created by either
+   devm_pci_epc_create() or pci_epc_create() using devm_pci_epc_destroy() or
+   pci_epc_destroy().
+
+*) pci_epc_linkup()
+
+   In order to notify all the function devices that the EPC device to which
+   they are linked has established a link with the host, the PCI controller
+   driver should invoke pci_epc_linkup().
+
+*) pci_epc_mem_init()
+
+   Initialize the pci_epc_mem structure used for allocating EPC addr space.
+
+*) pci_epc_mem_exit()
+
+   Cleanup the pci_epc_mem structure allocated during pci_epc_mem_init().
+
+2.1.2 APIs for the PCI Endpoint Function Driver
+
+This section lists the APIs that the PCI Endpoint core provides to be used
+by the PCI endpoint function driver.
+
+*) pci_epc_write_header()
+
+   The PCI endpoint function driver should use pci_epc_write_header() to
+   write the standard configuration header to the endpoint controller.
+
+*) pci_epc_set_bar()
+
+   The PCI endpoint function driver should use pci_epc_set_bar() to configure
+   the Base Address Register in order for the host to assign PCI addr space.
+   Register space of the function driver is usually configured
+   using this API.
+
+*) pci_epc_clear_bar()
+
+   The PCI endpoint function driver should use pci_epc_clear_bar() to reset
+   the BAR.
+
+*) pci_epc_raise_irq()
+
+   The PCI endpoint function driver should use pci_epc_raise_irq() to raise
+   Legacy Interrupt or MSI Interrupt.
+
+*) pci_epc_mem_alloc_addr()
+
+   The PCI endpoint function driver should use pci_epc_mem_alloc_addr(), to
+   allocate memory address from EPC addr space which is required to access
+   RC's buffer
+
+*) pci_epc_mem_free_addr()
+
+   The PCI endpoint function driver should use pci_epc_mem_free_addr() to
+   free the memory space allocated using pci_epc_mem_alloc_addr().
+
+2.1.3 Other APIs
+
+There are other APIs provided by the EPC library. These are used for binding
+the EPF device with EPC device. pci-ep-cfs.c can be used as reference for
+using these APIs.
+
+*) pci_epc_get()
+
+   Get a reference to the PCI endpoint controller based on the device name of
+   the controller.
+
+*) pci_epc_put()
+
+   Release the reference to the PCI endpoint controller obtained using
+   pci_epc_get()
+
+*) pci_epc_add_epf()
+
+   Add a PCI endpoint function to a PCI endpoint controller. A PCIe device
+   can have up to 8 functions according to the specification.
+
+*) pci_epc_remove_epf()
+
+   Remove the PCI endpoint function from PCI endpoint controller.
+
+*) pci_epc_start()
+
+   The PCI endpoint function driver should invoke pci_epc_start() once it
+   has configured the endpoint function and wants to start the PCI link.
+
+*) pci_epc_stop()
+
+   The PCI endpoint function driver should invoke pci_epc_stop() to stop
+   the PCI LINK.
+
+2.2 PCI Endpoint Function(EPF) Library
+
+The EPF library provides APIs to be used by the function driver and the EPC
+library to provide endpoint mode functionality.
+
+2.2.1 APIs for the PCI Endpoint Function Driver
+
+This section lists the APIs that the PCI Endpoint core provides to be used
+by the PCI endpoint function driver.
+
+*) pci_epf_register_driver()
+
+   The PCI Endpoint Function driver should implement the following ops:
+        * 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
+        * linkup: ops to perform when the EPC device has established a
+          connection with a host system
+
+  The PCI Function driver can then register the PCI EPF driver by using
+  pci_epf_register_driver().
+
+*) pci_epf_unregister_driver()
+
+  The PCI Function driver can unregister the PCI EPF driver by using
+  pci_epf_unregister_driver().
+
+*) pci_epf_alloc_space()
+
+  The PCI Function driver can allocate space for a particular BAR using
+  pci_epf_alloc_space().
+
+*) pci_epf_free_space()
+
+  The PCI Function driver can free the allocated space
+  (using pci_epf_alloc_space) by invoking pci_epf_free_space().
+
+2.2.2 APIs for the PCI Endpoint Controller Library
+This section lists the APIs that the PCI Endpoint core provides to be used
+by the PCI endpoint controller library.
+
+*) pci_epf_linkup()
+
+   The PCI endpoint controller library invokes pci_epf_linkup() when the
+   EPC device has established the connection to the host.
+
+2.2.2 Other APIs
+There are other APIs provided by the EPF library. These are used to notify
+the function driver when the EPF device is bound to the EPC device.
+pci-ep-cfs.c can be used as reference for using these APIs.
+
+*) pci_epf_create()
+
+   Create a new PCI EPF device by passing the name of the PCI EPF device.
+   This name will be used to bind the the EPF device to a EPF driver.
+
+*) pci_epf_destroy()
+
+   Destroy the created PCI EPF device.
+
+*) pci_epf_bind()
+
+   pci_epf_bind() should be invoked when the EPF device has been bound to
+   a EPC device.
+
+*) pci_epf_unbind()
+
+   pci_epf_unbind() should be invoked when the binding between EPC device
+   and EPF device is lost.
diff --git a/Documentation/PCI/endpoint/pci-test-function.txt b/Documentation/PCI/endpoint/pci-test-function.txt
new file mode 100644 (file)
index 0000000..0c519c9
--- /dev/null
@@ -0,0 +1,66 @@
+                               PCI TEST
+                   Kishon Vijay Abraham I <kishon@ti.com>
+
+Traditionally PCI RC has always been validated by using standard
+PCI cards like ethernet PCI cards or USB PCI cards or SATA PCI cards.
+However with the addition of EP-core in linux kernel, it is possible
+to configure a PCI controller that can operate in EP mode to work as
+a test device.
+
+The PCI endpoint test device is a virtual device (defined in software)
+used to test the endpoint functionality and serve as a sample driver
+for other PCI endpoint devices (to use the EP framework).
+
+The PCI endpoint test device has the following registers:
+
+       1) PCI_ENDPOINT_TEST_MAGIC
+       2) PCI_ENDPOINT_TEST_COMMAND
+       3) PCI_ENDPOINT_TEST_STATUS
+       4) PCI_ENDPOINT_TEST_SRC_ADDR
+       5) PCI_ENDPOINT_TEST_DST_ADDR
+       6) PCI_ENDPOINT_TEST_SIZE
+       7) PCI_ENDPOINT_TEST_CHECKSUM
+
+*) PCI_ENDPOINT_TEST_MAGIC
+
+This register will be used to test BAR0. A known pattern will be written
+and read back from MAGIC register to verify BAR0.
+
+*) PCI_ENDPOINT_TEST_COMMAND:
+
+This register will be used by the host driver to indicate the function
+that the endpoint device must perform.
+
+Bitfield Description:
+  Bit 0                : raise legacy IRQ
+  Bit 1                : raise MSI IRQ
+  Bit 2 - 7    : MSI interrupt number
+  Bit 8                : read command (read data from RC buffer)
+  Bit 9                : write command (write data to RC buffer)
+  Bit 10       : copy command (copy data from one RC buffer to another
+                 RC buffer)
+
+*) PCI_ENDPOINT_TEST_STATUS
+
+This register reflects the status of the PCI endpoint device.
+
+Bitfield Description:
+  Bit 0                : read success
+  Bit 1                : read fail
+  Bit 2                : write success
+  Bit 3                : write fail
+  Bit 4                : copy success
+  Bit 5                : copy fail
+  Bit 6                : IRQ raised
+  Bit 7                : source address is invalid
+  Bit 8                : destination address is invalid
+
+*) PCI_ENDPOINT_TEST_SRC_ADDR
+
+This register contains the source address (RC buffer address) for the
+COPY/READ command.
+
+*) PCI_ENDPOINT_TEST_DST_ADDR
+
+This register contains the destination address (RC buffer address) for
+the COPY/WRITE command.
diff --git a/Documentation/PCI/endpoint/pci-test-howto.txt b/Documentation/PCI/endpoint/pci-test-howto.txt
new file mode 100644 (file)
index 0000000..75f48c3
--- /dev/null
@@ -0,0 +1,179 @@
+                           PCI TEST USERGUIDE
+                   Kishon Vijay Abraham I <kishon@ti.com>
+
+This document is a guide to help users use pci-epf-test function driver
+and pci_endpoint_test host driver for testing PCI. The list of steps to
+be followed in the host side and EP side is given below.
+
+1. Endpoint Device
+
+1.1 Endpoint Controller Devices
+
+To find the list of endpoint controller devices in the system:
+
+       # ls /sys/class/pci_epc/
+         51000000.pcie_ep
+
+If PCI_ENDPOINT_CONFIGFS is enabled
+       # ls /sys/kernel/config/pci_ep/controllers
+         51000000.pcie_ep
+
+1.2 Endpoint Function Drivers
+
+To find the list of endpoint function drivers in the system:
+
+       # ls /sys/bus/pci-epf/drivers
+         pci_epf_test
+
+If PCI_ENDPOINT_CONFIGFS is enabled
+       # ls /sys/kernel/config/pci_ep/functions
+         pci_epf_test
+
+1.3 Creating pci-epf-test Device
+
+PCI endpoint function device can be created using the configfs. To create
+pci-epf-test device, the following commands can be used
+
+       # mount -t configfs none /sys/kernel/config
+       # cd /sys/kernel/config/pci_ep/
+       # mkdir functions/pci_epf_test/func1
+
+The "mkdir func1" above creates the pci-epf-test function device that will
+be probed by pci_epf_test driver.
+
+The PCI endpoint framework populates the directory with the following
+configurable fields.
+
+       # ls functions/pci_epf_test/func1
+         baseclass_code        interrupt_pin   revid           subsys_vendor_id
+         cache_line_size       msi_interrupts  subclass_code   vendorid
+         deviceid              progif_code     subsys_id
+
+The PCI endpoint function driver populates these entries with default values
+when the device is bound to the driver. The pci-epf-test driver populates
+vendorid with 0xffff and interrupt_pin with 0x0001
+
+       # cat functions/pci_epf_test/func1/vendorid
+         0xffff
+       # cat functions/pci_epf_test/func1/interrupt_pin
+         0x0001
+
+1.4 Configuring pci-epf-test Device
+
+The user can configure the pci-epf-test device using configfs entry. In order
+to change the vendorid and the number of MSI interrupts used by the function
+device, the following commands can be used.
+
+       # echo 0x104c > functions/pci_epf_test/func1/vendorid
+       # echo 0xb500 > functions/pci_epf_test/func1/deviceid
+       # echo 16 > functions/pci_epf_test/func1/msi_interrupts
+
+1.5 Binding pci-epf-test Device to EP Controller
+
+In order for the endpoint function device to be useful, it has to be bound to
+a PCI endpoint controller driver. Use the configfs to bind the function
+device to one of the controller driver present in the system.
+
+       # ln -s functions/pci_epf_test/func1 controllers/51000000.pcie_ep/
+
+Once the above step is completed, the PCI endpoint is ready to establish a link
+with the host.
+
+1.6 Start the Link
+
+In order for the endpoint device to establish a link with the host, the _start_
+field should be populated with '1'.
+
+       # echo 1 > controllers/51000000.pcie_ep/start
+
+2. RootComplex Device
+
+2.1 lspci Output
+
+Note that the devices listed here correspond to the value populated in 1.4 above
+
+       00:00.0 PCI bridge: Texas Instruments Device 8888 (rev 01)
+       01:00.0 Unassigned class [ff00]: Texas Instruments Device b500
+
+2.2 Using Endpoint Test function Device
+
+pcitest.sh added in tools/pci/ can be used to run all the default PCI endpoint
+tests. Before pcitest.sh can be used pcitest.c should be compiled using the
+following commands.
+
+       cd <kernel-dir>
+       make headers_install ARCH=arm
+       arm-linux-gnueabihf-gcc -Iusr/include tools/pci/pcitest.c -o pcitest
+       cp pcitest  <rootfs>/usr/sbin/
+       cp tools/pci/pcitest.sh <rootfs>
+
+2.2.1 pcitest.sh Output
+       # ./pcitest.sh
+       BAR tests
+
+       BAR0:           OKAY
+       BAR1:           OKAY
+       BAR2:           OKAY
+       BAR3:           OKAY
+       BAR4:           NOT OKAY
+       BAR5:           NOT OKAY
+
+       Interrupt tests
+
+       LEGACY IRQ:     NOT OKAY
+       MSI1:           OKAY
+       MSI2:           OKAY
+       MSI3:           OKAY
+       MSI4:           OKAY
+       MSI5:           OKAY
+       MSI6:           OKAY
+       MSI7:           OKAY
+       MSI8:           OKAY
+       MSI9:           OKAY
+       MSI10:          OKAY
+       MSI11:          OKAY
+       MSI12:          OKAY
+       MSI13:          OKAY
+       MSI14:          OKAY
+       MSI15:          OKAY
+       MSI16:          OKAY
+       MSI17:          NOT OKAY
+       MSI18:          NOT OKAY
+       MSI19:          NOT OKAY
+       MSI20:          NOT OKAY
+       MSI21:          NOT OKAY
+       MSI22:          NOT OKAY
+       MSI23:          NOT OKAY
+       MSI24:          NOT OKAY
+       MSI25:          NOT OKAY
+       MSI26:          NOT OKAY
+       MSI27:          NOT OKAY
+       MSI28:          NOT OKAY
+       MSI29:          NOT OKAY
+       MSI30:          NOT OKAY
+       MSI31:          NOT OKAY
+       MSI32:          NOT OKAY
+
+       Read Tests
+
+       READ (      1 bytes):           OKAY
+       READ (   1024 bytes):           OKAY
+       READ (   1025 bytes):           OKAY
+       READ (1024000 bytes):           OKAY
+       READ (1024001 bytes):           OKAY
+
+       Write Tests
+
+       WRITE (      1 bytes):          OKAY
+       WRITE (   1024 bytes):          OKAY
+       WRITE (   1025 bytes):          OKAY
+       WRITE (1024000 bytes):          OKAY
+       WRITE (1024001 bytes):          OKAY
+
+       Copy Tests
+
+       COPY (      1 bytes):           OKAY
+       COPY (   1024 bytes):           OKAY
+       COPY (   1025 bytes):           OKAY
+       COPY (1024000 bytes):           OKAY
+       COPY (1024001 bytes):           OKAY
index 1392c705cecae84c1bdbd601e27d91d78dc60980..b2480dd38c1135747c6bcceb5153df94a6a36326 100644 (file)
@@ -6,30 +6,40 @@ Required properties:
 - reg-names: Must be "config" for the PCIe configuration space.
     (The old way of getting the configuration address space from "ranges"
     is deprecated and should be avoided.)
+- num-lanes: number of lanes to use
+RC mode:
 - #address-cells: set to <3>
 - #size-cells: set to <2>
 - device_type: set to "pci"
 - ranges: ranges for the PCI memory and I/O regions
 - #interrupt-cells: set to <1>
-- interrupt-map-mask and interrupt-map: standard PCI properties
-       to define the mapping of the PCIe interface to interrupt
+- interrupt-map-mask and interrupt-map: standard PCI
+       properties to define the mapping of the PCIe interface to interrupt
        numbers.
-- num-lanes: number of lanes to use
+EP mode:
+- num-ib-windows: number of inbound address translation
+        windows
+- num-ob-windows: number of outbound address translation
+        windows
 
 Optional properties:
-- num-viewport: number of view ports configured in hardware.  If a platform
-  does not specify it, the driver assumes 2.
 - num-lanes: number of lanes to use (this property should be specified unless
   the link is brought already up in BIOS)
 - reset-gpio: gpio pin number of power good signal
-- bus-range: PCI bus numbers covered (it is recommended for new devicetrees to
-  specify this property, to keep backwards compatibility a range of 0x00-0xff
-  is assumed if not present)
 - clocks: Must contain an entry for each entry in clock-names.
        See ../clocks/clock-bindings.txt for details.
 - clock-names: Must include the following entries:
        - "pcie"
        - "pcie_bus"
+RC mode:
+- num-viewport: number of view ports configured in
+  hardware. If a platform does not specify it, the driver assumes 2.
+- bus-range: PCI bus numbers covered (it is recommended
+  for new devicetrees to specify this property, to keep backwards
+  compatibility a range of 0x00-0xff is assumed if not present)
+EP mode:
+- max-functions: maximum number of functions that can be
+  configured
 
 Example configuration:
 
diff --git a/Documentation/devicetree/bindings/pci/faraday,ftpci100.txt b/Documentation/devicetree/bindings/pci/faraday,ftpci100.txt
new file mode 100644 (file)
index 0000000..35d4a97
--- /dev/null
@@ -0,0 +1,129 @@
+Faraday Technology FTPCI100 PCI Host Bridge
+
+This PCI bridge is found inside that Cortina Systems Gemini SoC platform and
+is a generic IP block from Faraday Technology. It exists in two variants:
+plain and dual PCI. The plain version embeds a cascading interrupt controller
+into the host bridge. The dual version routes the interrupts to the host
+chips interrupt controller.
+
+The host controller appear on the PCI bus with vendor ID 0x159b (Faraday
+Technology) and product ID 0x4321.
+
+Mandatory properties:
+
+- compatible: ranging from specific to generic, should be one of
+  "cortina,gemini-pci", "faraday,ftpci100"
+  "cortina,gemini-pci-dual", "faraday,ftpci100-dual"
+  "faraday,ftpci100"
+  "faraday,ftpci100-dual"
+- reg: memory base and size for the host bridge
+- #address-cells: set to <3>
+- #size-cells: set to <2>
+- #interrupt-cells: set to <1>
+- bus-range: set to <0x00 0xff>
+- device_type, set to "pci"
+- ranges: see pci.txt
+- interrupt-map-mask: see pci.txt
+- interrupt-map: see pci.txt
+- dma-ranges: three ranges for the inbound memory region. The ranges must
+  be aligned to a 1MB boundary, and may be 1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB,
+  128MB, 256MB, 512MB, 1GB or 2GB in size. The memory should be marked as
+  pre-fetchable.
+
+Mandatory subnodes:
+- For "faraday,ftpci100" a node representing the interrupt-controller inside the
+  host bridge is mandatory. It has the following mandatory properties:
+  - interrupt: see interrupt-controller/interrupts.txt
+  - interrupt-parent: see interrupt-controller/interrupts.txt
+  - interrupt-controller: see interrupt-controller/interrupts.txt
+  - #address-cells: set to <0>
+  - #interrupt-cells: set to <1>
+
+I/O space considerations:
+
+The plain variant has 128MiB of non-prefetchable memory space, whereas the
+"dual" variant has 64MiB. Take this into account when describing the ranges.
+
+Interrupt map considerations:
+
+The "dual" variant will get INT A, B, C, D from the system interrupt controller
+and should point to respective interrupt in that controller in its
+interrupt-map.
+
+The code which is the only documentation of how the Faraday PCI (the non-dual
+variant) interrupts assigns the default interrupt mapping/swizzling has
+typically been like this, doing the swizzling on the interrupt controller side
+rather than in the interconnect:
+
+interrupt-map-mask = <0xf800 0 0 7>;
+interrupt-map =
+       <0x4800 0 0 1 &pci_intc 0>, /* Slot 9 */
+       <0x4800 0 0 2 &pci_intc 1>,
+       <0x4800 0 0 3 &pci_intc 2>,
+       <0x4800 0 0 4 &pci_intc 3>,
+       <0x5000 0 0 1 &pci_intc 1>, /* Slot 10 */
+       <0x5000 0 0 2 &pci_intc 2>,
+       <0x5000 0 0 3 &pci_intc 3>,
+       <0x5000 0 0 4 &pci_intc 0>,
+       <0x5800 0 0 1 &pci_intc 2>, /* Slot 11 */
+       <0x5800 0 0 2 &pci_intc 3>,
+       <0x5800 0 0 3 &pci_intc 0>,
+       <0x5800 0 0 4 &pci_intc 1>,
+       <0x6000 0 0 1 &pci_intc 3>, /* Slot 12 */
+       <0x6000 0 0 2 &pci_intc 0>,
+       <0x6000 0 0 3 &pci_intc 1>,
+       <0x6000 0 0 4 &pci_intc 2>;
+
+Example:
+
+pci@50000000 {
+       compatible = "cortina,gemini-pci", "faraday,ftpci100";
+       reg = <0x50000000 0x100>;
+       interrupts = <8 IRQ_TYPE_LEVEL_HIGH>, /* PCI A */
+                       <26 IRQ_TYPE_LEVEL_HIGH>, /* PCI B */
+                       <27 IRQ_TYPE_LEVEL_HIGH>, /* PCI C */
+                       <28 IRQ_TYPE_LEVEL_HIGH>; /* PCI D */
+       #address-cells = <3>;
+       #size-cells = <2>;
+       #interrupt-cells = <1>;
+
+       bus-range = <0x00 0xff>;
+       ranges = /* 1MiB I/O space 0x50000000-0x500fffff */
+                <0x01000000 0 0          0x50000000 0 0x00100000>,
+                /* 128MiB non-prefetchable memory 0x58000000-0x5fffffff */
+                <0x02000000 0 0x58000000 0x58000000 0 0x08000000>;
+
+       /* DMA ranges */
+       dma-ranges =
+       /* 128MiB at 0x00000000-0x07ffffff */
+       <0x02000000 0 0x00000000 0x00000000 0 0x08000000>,
+       /* 64MiB at 0x00000000-0x03ffffff */
+       <0x02000000 0 0x00000000 0x00000000 0 0x04000000>,
+       /* 64MiB at 0x00000000-0x03ffffff */
+       <0x02000000 0 0x00000000 0x00000000 0 0x04000000>;
+
+       interrupt-map-mask = <0xf800 0 0 7>;
+       interrupt-map =
+               <0x4800 0 0 1 &pci_intc 0>, /* Slot 9 */
+               <0x4800 0 0 2 &pci_intc 1>,
+               <0x4800 0 0 3 &pci_intc 2>,
+               <0x4800 0 0 4 &pci_intc 3>,
+               <0x5000 0 0 1 &pci_intc 1>, /* Slot 10 */
+               <0x5000 0 0 2 &pci_intc 2>,
+               <0x5000 0 0 3 &pci_intc 3>,
+               <0x5000 0 0 4 &pci_intc 0>,
+               <0x5800 0 0 1 &pci_intc 2>, /* Slot 11 */
+               <0x5800 0 0 2 &pci_intc 3>,
+               <0x5800 0 0 3 &pci_intc 0>,
+               <0x5800 0 0 4 &pci_intc 1>,
+               <0x6000 0 0 1 &pci_intc 3>, /* Slot 12 */
+               <0x6000 0 0 2 &pci_intc 0>,
+               <0x6000 0 0 3 &pci_intc 0>,
+               <0x6000 0 0 4 &pci_intc 0>;
+       pci_intc: interrupt-controller {
+               interrupt-parent = <&intcon>;
+               interrupt-controller;
+               #address-cells = <0>;
+               #interrupt-cells = <1>;
+       };
+};
index 83aeb1f5a645ce2fe494e14dedce27dc7085155f..e3d5680875b123326c7cc5527c8f85d7219fe691 100644 (file)
@@ -4,7 +4,11 @@ This PCIe host controller is based on the Synopsis Designware PCIe IP
 and thus inherits all the common properties defined in designware-pcie.txt.
 
 Required properties:
-- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie", "fsl,imx6qp-pcie"
+- compatible:
+       - "fsl,imx6q-pcie"
+       - "fsl,imx6sx-pcie",
+       - "fsl,imx6qp-pcie"
+       - "fsl,imx7d-pcie"
 - reg: base address and length of the PCIe controller
 - interrupts: A list of interrupt outputs of the controller. Must contain an
   entry for each entry in the interrupt-names property.
@@ -34,6 +38,14 @@ Additional required properties for imx6sx-pcie:
 - clock names: Must include the following additional entries:
        - "pcie_inbound_axi"
 
+Additional required properties for imx7d-pcie:
+- power-domains: Must be set to a phandle pointing to PCIE_PHY power domain
+- resets: Must contain phandles to PCIe-related reset lines exposed by SRC
+  IP block
+- reset-names: Must contain the following entires:
+              - "pciephy"
+              - "apps"
+
 Example:
 
        pcie@0x01000000 {
index 60e25161f35181bfd026b48519f43666d33f091e..6a07c96227e0a0a9bd96530c2833df195657e7dc 100644 (file)
@@ -1,17 +1,22 @@
 TI PCI Controllers
 
 PCIe Designware Controller
- - compatible: Should be "ti,dra7-pcie""
- - reg : Two register ranges as listed in the reg-names property
- - reg-names : The first entry must be "ti-conf" for the TI specific registers
-              The second entry must be "rc-dbics" for the designware pcie
-              registers
-              The third entry must be "config" for the PCIe configuration space
+ - compatible: Should be "ti,dra7-pcie" for RC
+              Should be "ti,dra7-pcie-ep" for EP
  - phys : list of PHY specifiers (used by generic PHY framework)
  - phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
               number of PHYs as specified in *phys* property.
  - ti,hwmods : Name of the hwmod associated to the pcie, "pcie<X>",
               where <X> is the instance number of the pcie from the HW spec.
+ - num-lanes as specified in ../designware-pcie.txt
+
+HOST MODE
+=========
+ - reg : Two register ranges as listed in the reg-names property
+ - reg-names : The first entry must be "ti-conf" for the TI specific registers
+              The second entry must be "rc-dbics" for the DesignWare PCIe
+              registers
+              The third entry must be "config" for the PCIe configuration space
  - interrupts : Two interrupt entries must be specified. The first one is for
                main interrupt line and the second for MSI interrupt line.
  - #address-cells,
@@ -19,13 +24,36 @@ PCIe Designware Controller
    #interrupt-cells,
    device_type,
    ranges,
-   num-lanes,
    interrupt-map-mask,
    interrupt-map : as specified in ../designware-pcie.txt
 
+DEVICE MODE
+===========
+ - reg : Four register ranges as listed in the reg-names property
+ - reg-names : "ti-conf" for the TI specific registers
+              "ep_dbics" for the standard configuration registers as
+               they are locally accessed within the DIF CS space
+              "ep_dbics2" for the standard configuration registers as
+               they are locally accessed within the DIF CS2 space
+              "addr_space" used to map remote RC address space
+ - interrupts : one interrupt entries must be specified for main interrupt.
+ - num-ib-windows : number of inbound address translation windows
+ - num-ob-windows : number of outbound address translation windows
+ - ti,syscon-unaligned-access: phandle to the syscon DT node. The 1st argument
+                              should contain the register offset within syscon
+                              and the 2nd argument should contain the bit field
+                              for setting the bit to enable unaligned
+                              access.
+
 Optional Property:
  - gpios : Should be added if a gpio line is required to drive PERST# line
 
+NOTE: Two DT nodes may be added for each PCI controller; one for host
+mode and another for device mode. So in order for PCI to
+work in host mode, EP mode DT node should be disabled and in order to PCI to
+work in EP mode, host mode DT node should be disabled. Host mode and EP
+mode are mutually exclusive.
+
 Example:
 axi {
        compatible = "simple-bus";
diff --git a/Documentation/misc-devices/pci-endpoint-test.txt b/Documentation/misc-devices/pci-endpoint-test.txt
new file mode 100644 (file)
index 0000000..4ebc359
--- /dev/null
@@ -0,0 +1,35 @@
+Driver for PCI Endpoint Test Function
+
+This driver should be used as a host side driver if the root complex is
+connected to a configurable PCI endpoint running *pci_epf_test* function
+driver configured according to [1].
+
+The "pci_endpoint_test" driver can be used to perform the following tests.
+
+The PCI driver for the test device performs the following tests
+       *) verifying addresses programmed in BAR
+       *) raise legacy IRQ
+       *) raise MSI IRQ
+       *) read data
+       *) write data
+       *) copy data
+
+This misc driver creates /dev/pci-endpoint-test.<num> for every
+*pci_epf_test* function connected to the root complex and "ioctls"
+should be used to perform the above tests.
+
+ioctl
+-----
+ PCITEST_BAR: Tests the BAR. The number of the BAR to be tested
+             should be passed as argument.
+ PCITEST_LEGACY_IRQ: Tests legacy IRQ
+ PCITEST_MSI: Tests message signalled interrupts. The MSI number
+             to be tested should be passed as argument.
+ PCITEST_WRITE: Perform write tests. The size of the buffer should be passed
+               as argument.
+ PCITEST_READ: Perform read tests. The size of the buffer should be passed
+              as argument.
+ PCITEST_COPY: Perform read tests. The size of the buffer should be passed
+              as argument.
+
+[1] -> Documentation/PCI/endpoint/function/binding/pci-test.txt
index c265a5fe48481f548629079cb529137e0a377f31..15ed8438909247652835663953fbe34257e5adc6 100644 (file)
@@ -9581,6 +9581,15 @@ F:       include/linux/pci*
 F:     arch/x86/pci/
 F:     arch/x86/kernel/quirks.c
 
+PCI ENDPOINT SUBSYSTEM
+M:     Kishon Vijay Abraham I <kishon@ti.com>
+L:     linux-pci@vger.kernel.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/pci-endpoint.git
+S:     Supported
+F:     drivers/pci/endpoint/
+F:     drivers/misc/pci_endpoint_test.c
+F:     tools/pci/
+
 PCI DRIVER FOR ALTERA PCIE IP
 M:     Ley Foon Tan <lftan@altera.com>
 L:     rfi@lists.rocketboards.org (moderated for non-subscribers)
index 6c679659cda5d502d97f31cf53fe8abce82ae22f..67ebff829cf258a3ec457d3b62e01a522f0d6a28 100644 (file)
@@ -524,7 +524,7 @@ static struct clockdomain pcie_7xx_clkdm = {
        .dep_bit          = DRA7XX_PCIE_STATDEP_SHIFT,
        .wkdep_srcs       = pcie_wkup_sleep_deps,
        .sleepdep_srcs    = pcie_wkup_sleep_deps,
-       .flags            = CLKDM_CAN_HWSUP_SWSUP,
+       .flags            = CLKDM_CAN_SWSUP,
 };
 
 static struct clockdomain atl_7xx_clkdm = {
index 2eced9afba5323ac16fa0f6a9a4459450f9f1fab..a5f8e43b2c4dc69ade5b8e4a7fdfd7ad543935f6 100644 (file)
@@ -14,7 +14,9 @@ obj-$(CONFIG_GENERIC_PHY)     += phy/
 obj-$(CONFIG_PINCTRL)          += pinctrl/
 obj-$(CONFIG_GPIOLIB)          += gpio/
 obj-y                          += pwm/
+
 obj-$(CONFIG_PCI)              += pci/
+obj-$(CONFIG_PCI_ENDPOINT)     += pci/endpoint/
 # PCI dwc controller drivers
 obj-y                          += pci/dwc/
 
index c290990d73edf87ece9b179ac3d845eef27531e9..527b115c4e232b165be1d4b801f6e0cd70e598ec 100644 (file)
@@ -771,6 +771,13 @@ config PANEL_BOOT_MESSAGE
 
 endif # PANEL
 
+config PCI_ENDPOINT_TEST
+       depends on PCI
+       tristate "PCI Endpoint Test driver"
+       ---help---
+           Enable this configuration option to enable the host side test driver
+           for PCI Endpoint.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
index 7a3ea89339b4d07c34289a539c4f9bd2f12d2aff..6e139cd704212e297968f004a17f90adb3a8360e 100644 (file)
@@ -54,6 +54,7 @@ obj-$(CONFIG_ECHO)            += echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)  += vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)         += cxl/
 obj-$(CONFIG_PANEL)             += panel.o
+obj-$(CONFIG_PCI_ENDPOINT_TEST)        += pci_endpoint_test.o
 
 lkdtm-$(CONFIG_LKDTM)          += lkdtm_core.o
 lkdtm-$(CONFIG_LKDTM)          += lkdtm_bugs.o
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
new file mode 100644 (file)
index 0000000..09c10f4
--- /dev/null
@@ -0,0 +1,534 @@
+/**
+ * Host side test driver to test endpoint functionality
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+
+#include <linux/pci_regs.h>
+
+#include <uapi/linux/pcitest.h>
+
+#define DRV_MODULE_NAME                        "pci-endpoint-test"
+
+#define PCI_ENDPOINT_TEST_MAGIC                0x0
+
+#define PCI_ENDPOINT_TEST_COMMAND      0x4
+#define COMMAND_RAISE_LEGACY_IRQ       BIT(0)
+#define COMMAND_RAISE_MSI_IRQ          BIT(1)
+#define MSI_NUMBER_SHIFT               2
+/* 6 bits for MSI number */
+#define COMMAND_READ                    BIT(8)
+#define COMMAND_WRITE                   BIT(9)
+#define COMMAND_COPY                    BIT(10)
+
+#define PCI_ENDPOINT_TEST_STATUS       0x8
+#define STATUS_READ_SUCCESS             BIT(0)
+#define STATUS_READ_FAIL                BIT(1)
+#define STATUS_WRITE_SUCCESS            BIT(2)
+#define STATUS_WRITE_FAIL               BIT(3)
+#define STATUS_COPY_SUCCESS             BIT(4)
+#define STATUS_COPY_FAIL                BIT(5)
+#define STATUS_IRQ_RAISED               BIT(6)
+#define STATUS_SRC_ADDR_INVALID         BIT(7)
+#define STATUS_DST_ADDR_INVALID         BIT(8)
+
+#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR       0xc
+#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR       0x10
+
+#define PCI_ENDPOINT_TEST_LOWER_DST_ADDR       0x14
+#define PCI_ENDPOINT_TEST_UPPER_DST_ADDR       0x18
+
+#define PCI_ENDPOINT_TEST_SIZE         0x1c
+#define PCI_ENDPOINT_TEST_CHECKSUM     0x20
+
+static DEFINE_IDA(pci_endpoint_test_ida);
+
+#define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
+                                           miscdev)
+enum pci_barno {
+       BAR_0,
+       BAR_1,
+       BAR_2,
+       BAR_3,
+       BAR_4,
+       BAR_5,
+};
+
+struct pci_endpoint_test {
+       struct pci_dev  *pdev;
+       void __iomem    *base;
+       void __iomem    *bar[6];
+       struct completion irq_raised;
+       int             last_irq;
+       /* mutex to protect the ioctls */
+       struct mutex    mutex;
+       struct miscdevice miscdev;
+};
+
+static int bar_size[] = { 4, 512, 1024, 16384, 131072, 1048576 };
+
+static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test,
+                                         u32 offset)
+{
+       return readl(test->base + offset);
+}
+
+static inline void pci_endpoint_test_writel(struct pci_endpoint_test *test,
+                                           u32 offset, u32 value)
+{
+       writel(value, test->base + offset);
+}
+
+static inline u32 pci_endpoint_test_bar_readl(struct pci_endpoint_test *test,
+                                             int bar, int offset)
+{
+       return readl(test->bar[bar] + offset);
+}
+
+static inline void pci_endpoint_test_bar_writel(struct pci_endpoint_test *test,
+                                               int bar, u32 offset, u32 value)
+{
+       writel(value, test->bar[bar] + offset);
+}
+
+static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
+{
+       struct pci_endpoint_test *test = dev_id;
+       u32 reg;
+
+       reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+       if (reg & STATUS_IRQ_RAISED) {
+               test->last_irq = irq;
+               complete(&test->irq_raised);
+               reg &= ~STATUS_IRQ_RAISED;
+       }
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS,
+                                reg);
+
+       return IRQ_HANDLED;
+}
+
+static bool pci_endpoint_test_bar(struct pci_endpoint_test *test,
+                                 enum pci_barno barno)
+{
+       int j;
+       u32 val;
+       int size;
+
+       if (!test->bar[barno])
+               return false;
+
+       size = bar_size[barno];
+
+       for (j = 0; j < size; j += 4)
+               pci_endpoint_test_bar_writel(test, barno, j, 0xA0A0A0A0);
+
+       for (j = 0; j < size; j += 4) {
+               val = pci_endpoint_test_bar_readl(test, barno, j);
+               if (val != 0xA0A0A0A0)
+                       return false;
+       }
+
+       return true;
+}
+
+static bool pci_endpoint_test_legacy_irq(struct pci_endpoint_test *test)
+{
+       u32 val;
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                COMMAND_RAISE_LEGACY_IRQ);
+       val = wait_for_completion_timeout(&test->irq_raised,
+                                         msecs_to_jiffies(1000));
+       if (!val)
+               return false;
+
+       return true;
+}
+
+static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test,
+                                     u8 msi_num)
+{
+       u32 val;
+       struct pci_dev *pdev = test->pdev;
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                msi_num << MSI_NUMBER_SHIFT |
+                                COMMAND_RAISE_MSI_IRQ);
+       val = wait_for_completion_timeout(&test->irq_raised,
+                                         msecs_to_jiffies(1000));
+       if (!val)
+               return false;
+
+       if (test->last_irq - pdev->irq == msi_num - 1)
+               return true;
+
+       return false;
+}
+
+static bool pci_endpoint_test_copy(struct pci_endpoint_test *test, size_t size)
+{
+       bool ret = false;
+       void *src_addr;
+       void *dst_addr;
+       dma_addr_t src_phys_addr;
+       dma_addr_t dst_phys_addr;
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+       u32 src_crc32;
+       u32 dst_crc32;
+
+       src_addr = dma_alloc_coherent(dev, size, &src_phys_addr, GFP_KERNEL);
+       if (!src_addr) {
+               dev_err(dev, "failed to allocate source buffer\n");
+               ret = false;
+               goto err;
+       }
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
+                                lower_32_bits(src_phys_addr));
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
+                                upper_32_bits(src_phys_addr));
+
+       get_random_bytes(src_addr, size);
+       src_crc32 = crc32_le(~0, src_addr, size);
+
+       dst_addr = dma_alloc_coherent(dev, size, &dst_phys_addr, GFP_KERNEL);
+       if (!dst_addr) {
+               dev_err(dev, "failed to allocate destination address\n");
+               ret = false;
+               goto err_src_addr;
+       }
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
+                                lower_32_bits(dst_phys_addr));
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
+                                upper_32_bits(dst_phys_addr));
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE,
+                                size);
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                1 << MSI_NUMBER_SHIFT | COMMAND_COPY);
+
+       wait_for_completion(&test->irq_raised);
+
+       dst_crc32 = crc32_le(~0, dst_addr, size);
+       if (dst_crc32 == src_crc32)
+               ret = true;
+
+       dma_free_coherent(dev, size, dst_addr, dst_phys_addr);
+
+err_src_addr:
+       dma_free_coherent(dev, size, src_addr, src_phys_addr);
+
+err:
+       return ret;
+}
+
+static bool pci_endpoint_test_write(struct pci_endpoint_test *test, size_t size)
+{
+       bool ret = false;
+       u32 reg;
+       void *addr;
+       dma_addr_t phys_addr;
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+       u32 crc32;
+
+       addr = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
+       if (!addr) {
+               dev_err(dev, "failed to allocate address\n");
+               ret = false;
+               goto err;
+       }
+
+       get_random_bytes(addr, size);
+
+       crc32 = crc32_le(~0, addr, size);
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_CHECKSUM,
+                                crc32);
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_SRC_ADDR,
+                                lower_32_bits(phys_addr));
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_SRC_ADDR,
+                                upper_32_bits(phys_addr));
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                1 << MSI_NUMBER_SHIFT | COMMAND_READ);
+
+       wait_for_completion(&test->irq_raised);
+
+       reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+       if (reg & STATUS_READ_SUCCESS)
+               ret = true;
+
+       dma_free_coherent(dev, size, addr, phys_addr);
+
+err:
+       return ret;
+}
+
+static bool pci_endpoint_test_read(struct pci_endpoint_test *test, size_t size)
+{
+       bool ret = false;
+       void *addr;
+       dma_addr_t phys_addr;
+       struct pci_dev *pdev = test->pdev;
+       struct device *dev = &pdev->dev;
+       u32 crc32;
+
+       addr = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
+       if (!addr) {
+               dev_err(dev, "failed to allocate destination address\n");
+               ret = false;
+               goto err;
+       }
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_LOWER_DST_ADDR,
+                                lower_32_bits(phys_addr));
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_UPPER_DST_ADDR,
+                                upper_32_bits(phys_addr));
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, size);
+
+       pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
+                                1 << MSI_NUMBER_SHIFT | COMMAND_WRITE);
+
+       wait_for_completion(&test->irq_raised);
+
+       crc32 = crc32_le(~0, addr, size);
+       if (crc32 == pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM))
+               ret = true;
+
+       dma_free_coherent(dev, size, addr, phys_addr);
+err:
+       return ret;
+}
+
+static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
+                                   unsigned long arg)
+{
+       int ret = -EINVAL;
+       enum pci_barno bar;
+       struct pci_endpoint_test *test = to_endpoint_test(file->private_data);
+
+       mutex_lock(&test->mutex);
+       switch (cmd) {
+       case PCITEST_BAR:
+               bar = arg;
+               if (bar < 0 || bar > 5)
+                       goto ret;
+               ret = pci_endpoint_test_bar(test, bar);
+               break;
+       case PCITEST_LEGACY_IRQ:
+               ret = pci_endpoint_test_legacy_irq(test);
+               break;
+       case PCITEST_MSI:
+               ret = pci_endpoint_test_msi_irq(test, arg);
+               break;
+       case PCITEST_WRITE:
+               ret = pci_endpoint_test_write(test, arg);
+               break;
+       case PCITEST_READ:
+               ret = pci_endpoint_test_read(test, arg);
+               break;
+       case PCITEST_COPY:
+               ret = pci_endpoint_test_copy(test, arg);
+               break;
+       }
+
+ret:
+       mutex_unlock(&test->mutex);
+       return ret;
+}
+
+static const struct file_operations pci_endpoint_test_fops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = pci_endpoint_test_ioctl,
+};
+
+static int pci_endpoint_test_probe(struct pci_dev *pdev,
+                                  const struct pci_device_id *ent)
+{
+       int i;
+       int err;
+       int irq;
+       int id;
+       char name[20];
+       enum pci_barno bar;
+       void __iomem *base;
+       struct device *dev = &pdev->dev;
+       struct pci_endpoint_test *test;
+       struct miscdevice *misc_device;
+
+       if (pci_is_bridge(pdev))
+               return -ENODEV;
+
+       test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL);
+       if (!test)
+               return -ENOMEM;
+
+       test->pdev = pdev;
+       init_completion(&test->irq_raised);
+       mutex_init(&test->mutex);
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(dev, "Cannot enable PCI device\n");
+               return err;
+       }
+
+       err = pci_request_regions(pdev, DRV_MODULE_NAME);
+       if (err) {
+               dev_err(dev, "Cannot obtain PCI resources\n");
+               goto err_disable_pdev;
+       }
+
+       pci_set_master(pdev);
+
+       irq = pci_alloc_irq_vectors(pdev, 1, 32, PCI_IRQ_MSI);
+       if (irq < 0)
+               dev_err(dev, "failed to get MSI interrupts\n");
+
+       err = devm_request_irq(dev, pdev->irq, pci_endpoint_test_irqhandler,
+                              IRQF_SHARED, DRV_MODULE_NAME, test);
+       if (err) {
+               dev_err(dev, "failed to request IRQ %d\n", pdev->irq);
+               goto err_disable_msi;
+       }
+
+       for (i = 1; i < irq; i++) {
+               err = devm_request_irq(dev, pdev->irq + i,
+                                      pci_endpoint_test_irqhandler,
+                                      IRQF_SHARED, DRV_MODULE_NAME, test);
+               if (err)
+                       dev_err(dev, "failed to request IRQ %d for MSI %d\n",
+                               pdev->irq + i, i + 1);
+       }
+
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+               base = pci_ioremap_bar(pdev, bar);
+               if (!base) {
+                       dev_err(dev, "failed to read BAR%d\n", bar);
+                       WARN_ON(bar == BAR_0);
+               }
+               test->bar[bar] = base;
+       }
+
+       test->base = test->bar[0];
+       if (!test->base) {
+               dev_err(dev, "Cannot perform PCI test without BAR0\n");
+               goto err_iounmap;
+       }
+
+       pci_set_drvdata(pdev, test);
+
+       id = ida_simple_get(&pci_endpoint_test_ida, 0, 0, GFP_KERNEL);
+       if (id < 0) {
+               dev_err(dev, "unable to get id\n");
+               goto err_iounmap;
+       }
+
+       snprintf(name, sizeof(name), DRV_MODULE_NAME ".%d", id);
+       misc_device = &test->miscdev;
+       misc_device->minor = MISC_DYNAMIC_MINOR;
+       misc_device->name = name;
+       misc_device->fops = &pci_endpoint_test_fops,
+
+       err = misc_register(misc_device);
+       if (err) {
+               dev_err(dev, "failed to register device\n");
+               goto err_ida_remove;
+       }
+
+       return 0;
+
+err_ida_remove:
+       ida_simple_remove(&pci_endpoint_test_ida, id);
+
+err_iounmap:
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+               if (test->bar[bar])
+                       pci_iounmap(pdev, test->bar[bar]);
+       }
+
+err_disable_msi:
+       pci_disable_msi(pdev);
+       pci_release_regions(pdev);
+
+err_disable_pdev:
+       pci_disable_device(pdev);
+
+       return err;
+}
+
+static void pci_endpoint_test_remove(struct pci_dev *pdev)
+{
+       int id;
+       enum pci_barno bar;
+       struct pci_endpoint_test *test = pci_get_drvdata(pdev);
+       struct miscdevice *misc_device = &test->miscdev;
+
+       if (sscanf(misc_device->name, DRV_MODULE_NAME ".%d", &id) != 1)
+               return;
+
+       misc_deregister(&test->miscdev);
+       ida_simple_remove(&pci_endpoint_test_ida, id);
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+               if (test->bar[bar])
+                       pci_iounmap(pdev, test->bar[bar]);
+       }
+       pci_disable_msi(pdev);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+}
+
+static const struct pci_device_id pci_endpoint_test_tbl[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
+       { PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
+
+static struct pci_driver pci_endpoint_test_driver = {
+       .name           = DRV_MODULE_NAME,
+       .id_table       = pci_endpoint_test_tbl,
+       .probe          = pci_endpoint_test_probe,
+       .remove         = pci_endpoint_test_remove,
+};
+module_pci_driver(pci_endpoint_test_driver);
+
+MODULE_DESCRIPTION("PCI ENDPOINT TEST HOST DRIVER");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
index df141420c902ae5ea45972021a121ac6917cd119..9747c1ec8c74389cc152a630a57b68560854ce59 100644 (file)
@@ -134,3 +134,4 @@ config PCI_HYPERV
 source "drivers/pci/hotplug/Kconfig"
 source "drivers/pci/dwc/Kconfig"
 source "drivers/pci/host/Kconfig"
+source "drivers/pci/endpoint/Kconfig"
index dfb8a69afc28f5b26a86bb024356c3ea03fdaf31..b7e15526d676504f61f4baa160e431171f19b0fd 100644 (file)
@@ -9,16 +9,44 @@ config PCIE_DW_HOST
        depends on PCI_MSI_IRQ_DOMAIN
         select PCIE_DW
 
+config PCIE_DW_EP
+       bool
+       depends on PCI_ENDPOINT
+       select PCIE_DW
+
 config PCI_DRA7XX
        bool "TI DRA7xx PCIe controller"
-       depends on PCI
+       depends on (PCI && PCI_MSI_IRQ_DOMAIN) || PCI_ENDPOINT
        depends on OF && HAS_IOMEM && TI_PIPE3
+       help
+        Enables support for the PCIe controller in the DRA7xx SoC. There
+        are two instances of PCIe controller in DRA7xx. This controller can
+        work either as EP or RC. In order to enable host-specific features
+        PCI_DRA7XX_HOST must be selected and in order to enable device-
+        specific features PCI_DRA7XX_EP must be selected. This uses
+        the Designware core.
+
+if PCI_DRA7XX
+
+config PCI_DRA7XX_HOST
+       bool "PCI DRA7xx Host Mode"
+       depends on PCI
        depends on PCI_MSI_IRQ_DOMAIN
        select PCIE_DW_HOST
+       default y
        help
-        Enables support for the PCIe controller in the DRA7xx SoC.  There
-        are two instances of PCIe controller in DRA7xx.  This controller can
-        act both as EP and RC.  This reuses the Designware core.
+        Enables support for the PCIe controller in the DRA7xx SoC to work in
+        host mode.
+
+config PCI_DRA7XX_EP
+       bool "PCI DRA7xx Endpoint Mode"
+       depends on PCI_ENDPOINT
+       select PCIE_DW_EP
+       help
+        Enables support for the PCIe controller in the DRA7xx SoC to work in
+        endpoint mode.
+
+endif
 
 config PCIE_DW_PLAT
        bool "Platform bus based DesignWare PCIe Controller"
@@ -89,6 +117,7 @@ config PCI_HISI
        depends on PCI_MSI_IRQ_DOMAIN
        select PCIEPORTBUS
        select PCIE_DW_HOST
+       select PCI_HOST_COMMON
        help
          Say Y here if you want PCIe controller support on HiSilicon
          Hip05 and Hip06 SoCs
index a2df13c287989fa4d4b71a2283798ee7e052ba18..f31a8596442ae19d164cf02ca10c97b64715fc76 100644 (file)
@@ -1,7 +1,10 @@
 obj-$(CONFIG_PCIE_DW) += pcie-designware.o
 obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o
+obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o
 obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
-obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
+ifneq ($(filter y,$(CONFIG_PCI_DRA7XX_HOST) $(CONFIG_PCI_DRA7XX_EP)),)
+        obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
+endif
 obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
 obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
 obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
index 0984baff07e3490c3139e32ac29a0929cead52f0..8decf46cf5257228985ae722ec3ddaa3ca9b2184 100644 (file)
  * published by the Free Software Foundation.
  */
 
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/of_device.h>
 #include <linux/of_gpio.h>
 #include <linux/of_pci.h>
 #include <linux/pci.h>
@@ -24,6 +26,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/resource.h>
 #include <linux/types.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 #include "pcie-designware.h"
 
 #define        MSI                                             BIT(4)
 #define        LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD)
 
+#define        PCIECTRL_TI_CONF_DEVICE_TYPE                    0x0100
+#define        DEVICE_TYPE_EP                                  0x0
+#define        DEVICE_TYPE_LEG_EP                              0x1
+#define        DEVICE_TYPE_RC                                  0x4
+
 #define        PCIECTRL_DRA7XX_CONF_DEVICE_CMD                 0x0104
 #define        LTSSM_EN                                        0x1
 
 
 #define EXP_CAP_ID_OFFSET                              0x70
 
+#define        PCIECTRL_TI_CONF_INTX_ASSERT                    0x0124
+#define        PCIECTRL_TI_CONF_INTX_DEASSERT                  0x0128
+
+#define        PCIECTRL_TI_CONF_MSI_XMT                        0x012c
+#define MSI_REQ_GRANT                                  BIT(0)
+#define MSI_VECTOR_SHIFT                               7
+
 struct dra7xx_pcie {
        struct dw_pcie          *pci;
        void __iomem            *base;          /* DT ti_conf */
@@ -73,6 +89,11 @@ struct dra7xx_pcie {
        struct phy              **phy;
        int                     link_gen;
        struct irq_domain       *irq_domain;
+       enum dw_pcie_device_mode mode;
+};
+
+struct dra7xx_pcie_of_data {
+       enum dw_pcie_device_mode mode;
 };
 
 #define to_dra7xx_pcie(x)      dev_get_drvdata((x)->dev)
@@ -88,6 +109,11 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset,
        writel(value, pcie->base + offset);
 }
 
+static u64 dra7xx_pcie_cpu_addr_fixup(u64 pci_addr)
+{
+       return pci_addr & DRA7XX_CPU_TO_BUS_ADDR;
+}
+
 static int dra7xx_pcie_link_up(struct dw_pcie *pci)
 {
        struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
@@ -96,9 +122,19 @@ static int dra7xx_pcie_link_up(struct dw_pcie *pci)
        return !!(reg & LINK_UP);
 }
 
-static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
+static void dra7xx_pcie_stop_link(struct dw_pcie *pci)
 {
-       struct dw_pcie *pci = dra7xx->pci;
+       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
+       u32 reg;
+
+       reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
+       reg &= ~LTSSM_EN;
+       dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
+}
+
+static int dra7xx_pcie_establish_link(struct dw_pcie *pci)
+{
+       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
        struct device *dev = pci->dev;
        u32 reg;
        u32 exp_cap_off = EXP_CAP_ID_OFFSET;
@@ -132,34 +168,42 @@ static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
        reg |= LTSSM_EN;
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
 
-       return dw_pcie_wait_for_link(pci);
+       return 0;
 }
 
-static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
+static void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx)
 {
-       dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
-                          ~INTERRUPTS);
-       dra7xx_pcie_writel(dra7xx,
-                          PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS);
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI,
                           ~LEG_EP_INTERRUPTS & ~MSI);
-       dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
+
+       dra7xx_pcie_writel(dra7xx,
+                          PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
                           MSI | LEG_EP_INTERRUPTS);
 }
 
+static void dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie *dra7xx)
+{
+       dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
+                          ~INTERRUPTS);
+       dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN,
+                          INTERRUPTS);
+}
+
+static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
+{
+       dra7xx_pcie_enable_wrapper_interrupts(dra7xx);
+       dra7xx_pcie_enable_msi_interrupts(dra7xx);
+}
+
 static void dra7xx_pcie_host_init(struct pcie_port *pp)
 {
        struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
        struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
 
-       pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
-       pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
-       pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR;
-       pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR;
-
        dw_pcie_setup_rc(pp);
 
-       dra7xx_pcie_establish_link(dra7xx);
+       dra7xx_pcie_establish_link(pci);
+       dw_pcie_wait_for_link(pci);
        dw_pcie_msi_init(pp);
        dra7xx_pcie_enable_interrupts(dra7xx);
 }
@@ -237,6 +281,7 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
        struct dra7xx_pcie *dra7xx = arg;
        struct dw_pcie *pci = dra7xx->pci;
        struct device *dev = pci->dev;
+       struct dw_pcie_ep *ep = &pci->ep;
        u32 reg;
 
        reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN);
@@ -273,8 +318,11 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
        if (reg & LINK_REQ_RST)
                dev_dbg(dev, "Link Request Reset\n");
 
-       if (reg & LINK_UP_EVT)
+       if (reg & LINK_UP_EVT) {
+               if (dra7xx->mode == DW_PCIE_EP_TYPE)
+                       dw_pcie_ep_linkup(ep);
                dev_dbg(dev, "Link-up state change\n");
+       }
 
        if (reg & CFG_BME_EVT)
                dev_dbg(dev, "CFG 'Bus Master Enable' change\n");
@@ -287,6 +335,94 @@ static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
+static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
+
+       dra7xx_pcie_enable_wrapper_interrupts(dra7xx);
+}
+
+static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx)
+{
+       dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1);
+       mdelay(1);
+       dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_DEASSERT, 0x1);
+}
+
+static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx,
+                                     u8 interrupt_num)
+{
+       u32 reg;
+
+       reg = (interrupt_num - 1) << MSI_VECTOR_SHIFT;
+       reg |= MSI_REQ_GRANT;
+       dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg);
+}
+
+static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep,
+                                enum pci_epc_irq_type type, u8 interrupt_num)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
+
+       switch (type) {
+       case PCI_EPC_IRQ_LEGACY:
+               dra7xx_pcie_raise_legacy_irq(dra7xx);
+               break;
+       case PCI_EPC_IRQ_MSI:
+               dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num);
+               break;
+       default:
+               dev_err(pci->dev, "UNKNOWN IRQ type\n");
+       }
+
+       return 0;
+}
+
+static struct dw_pcie_ep_ops pcie_ep_ops = {
+       .ep_init = dra7xx_pcie_ep_init,
+       .raise_irq = dra7xx_pcie_raise_irq,
+};
+
+static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
+                                    struct platform_device *pdev)
+{
+       int ret;
+       struct dw_pcie_ep *ep;
+       struct resource *res;
+       struct device *dev = &pdev->dev;
+       struct dw_pcie *pci = dra7xx->pci;
+
+       ep = &pci->ep;
+       ep->ops = &pcie_ep_ops;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics");
+       pci->dbi_base = devm_ioremap(dev, res->start, resource_size(res));
+       if (!pci->dbi_base)
+               return -ENOMEM;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ep_dbics2");
+       pci->dbi_base2 = devm_ioremap(dev, res->start, resource_size(res));
+       if (!pci->dbi_base2)
+               return -ENOMEM;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
+       if (!res)
+               return -EINVAL;
+
+       ep->phys_base = res->start;
+       ep->addr_size = resource_size(res);
+
+       ret = dw_pcie_ep_init(ep);
+       if (ret) {
+               dev_err(dev, "failed to initialize endpoint\n");
+               return ret;
+       }
+
+       return 0;
+}
+
 static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
                                       struct platform_device *pdev)
 {
@@ -329,6 +465,9 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
 }
 
 static const struct dw_pcie_ops dw_pcie_ops = {
+       .cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup,
+       .start_link = dra7xx_pcie_establish_link,
+       .stop_link = dra7xx_pcie_stop_link,
        .link_up = dra7xx_pcie_link_up,
 };
 
@@ -371,6 +510,68 @@ err_phy:
        return ret;
 }
 
+static const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = {
+       .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = {
+       .mode = DW_PCIE_EP_TYPE,
+};
+
+static const struct of_device_id of_dra7xx_pcie_match[] = {
+       {
+               .compatible = "ti,dra7-pcie",
+               .data = &dra7xx_pcie_rc_of_data,
+       },
+       {
+               .compatible = "ti,dra7-pcie-ep",
+               .data = &dra7xx_pcie_ep_of_data,
+       },
+       {},
+};
+
+/*
+ * dra7xx_pcie_ep_unaligned_memaccess: workaround for AM572x/AM571x Errata i870
+ * @dra7xx: the dra7xx device where the workaround should be applied
+ *
+ * Access to the PCIe slave port that are not 32-bit aligned will result
+ * in incorrect mapping to TLP Address and Byte enable fields. Therefore,
+ * byte and half-word accesses are not possible to byte offset 0x1, 0x2, or
+ * 0x3.
+ *
+ * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1.
+ */
+static int dra7xx_pcie_ep_unaligned_memaccess(struct device *dev)
+{
+       int ret;
+       struct device_node *np = dev->of_node;
+       struct of_phandle_args args;
+       struct regmap *regmap;
+
+       regmap = syscon_regmap_lookup_by_phandle(np,
+                                                "ti,syscon-unaligned-access");
+       if (IS_ERR(regmap)) {
+               dev_dbg(dev, "can't get ti,syscon-unaligned-access\n");
+               return -EINVAL;
+       }
+
+       ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-unaligned-access",
+                                              2, 0, &args);
+       if (ret) {
+               dev_err(dev, "failed to parse ti,syscon-unaligned-access\n");
+               return ret;
+       }
+
+       ret = regmap_update_bits(regmap, args.args[0], args.args[1],
+                                args.args[1]);
+       if (ret)
+               dev_err(dev, "failed to enable unaligned access\n");
+
+       of_node_put(args.np);
+
+       return ret;
+}
+
 static int __init dra7xx_pcie_probe(struct platform_device *pdev)
 {
        u32 reg;
@@ -388,6 +589,16 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        struct device_node *np = dev->of_node;
        char name[10];
        struct gpio_desc *reset;
+       const struct of_device_id *match;
+       const struct dra7xx_pcie_of_data *data;
+       enum dw_pcie_device_mode mode;
+
+       match = of_match_device(of_match_ptr(of_dra7xx_pcie_match), dev);
+       if (!match)
+               return -EINVAL;
+
+       data = (struct dra7xx_pcie_of_data *)match->data;
+       mode = (enum dw_pcie_device_mode)data->mode;
 
        dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);
        if (!dra7xx)
@@ -409,13 +620,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler,
-                              IRQF_SHARED, "dra7xx-pcie-main", dra7xx);
-       if (ret) {
-               dev_err(dev, "failed to request irq\n");
-               return ret;
-       }
-
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf");
        base = devm_ioremap_nocache(dev, res->start, resource_size(res));
        if (!base)
@@ -473,9 +677,37 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        if (dra7xx->link_gen < 0 || dra7xx->link_gen > 2)
                dra7xx->link_gen = 2;
 
-       ret = dra7xx_add_pcie_port(dra7xx, pdev);
-       if (ret < 0)
+       switch (mode) {
+       case DW_PCIE_RC_TYPE:
+               dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
+                                  DEVICE_TYPE_RC);
+               ret = dra7xx_add_pcie_port(dra7xx, pdev);
+               if (ret < 0)
+                       goto err_gpio;
+               break;
+       case DW_PCIE_EP_TYPE:
+               dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
+                                  DEVICE_TYPE_EP);
+
+               ret = dra7xx_pcie_ep_unaligned_memaccess(dev);
+               if (ret)
+                       goto err_gpio;
+
+               ret = dra7xx_add_pcie_ep(dra7xx, pdev);
+               if (ret < 0)
+                       goto err_gpio;
+               break;
+       default:
+               dev_err(dev, "INVALID device type %d\n", mode);
+       }
+       dra7xx->mode = mode;
+
+       ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler,
+                              IRQF_SHARED, "dra7xx-pcie-main", dra7xx);
+       if (ret) {
+               dev_err(dev, "failed to request irq\n");
                goto err_gpio;
+       }
 
        return 0;
 
@@ -496,6 +728,9 @@ static int dra7xx_pcie_suspend(struct device *dev)
        struct dw_pcie *pci = dra7xx->pci;
        u32 val;
 
+       if (dra7xx->mode != DW_PCIE_RC_TYPE)
+               return 0;
+
        /* clear MSE */
        val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
        val &= ~PCI_COMMAND_MEMORY;
@@ -510,6 +745,9 @@ static int dra7xx_pcie_resume(struct device *dev)
        struct dw_pcie *pci = dra7xx->pci;
        u32 val;
 
+       if (dra7xx->mode != DW_PCIE_RC_TYPE)
+               return 0;
+
        /* set MSE */
        val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
        val |= PCI_COMMAND_MEMORY;
@@ -548,11 +786,6 @@ static const struct dev_pm_ops dra7xx_pcie_pm_ops = {
                                      dra7xx_pcie_resume_noirq)
 };
 
-static const struct of_device_id of_dra7xx_pcie_match[] = {
-       { .compatible = "ti,dra7-pcie", },
-       {},
-};
-
 static struct platform_driver dra7xx_pcie_driver = {
        .driver = {
                .name   = "dra7-pcie",
index 993b650ef2759cbffc56c0bc086a8d2172ef4fcf..546082ad5a3f767d97af9d6015023aded654032e 100644 (file)
@@ -132,10 +132,6 @@ static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
        struct device *dev = pci->dev;
        struct resource *res;
 
-       /* If using the PHY framework, doesn't need to get other resource */
-       if (ep->using_phy)
-               return 0;
-
        ep->mem_res = devm_kzalloc(dev, sizeof(*ep->mem_res), GFP_KERNEL);
        if (!ep->mem_res)
                return -ENOMEM;
@@ -145,6 +141,10 @@ static int exynos5440_pcie_get_mem_resources(struct platform_device *pdev,
        if (IS_ERR(ep->mem_res->elbi_base))
                return PTR_ERR(ep->mem_res->elbi_base);
 
+       /* If using the PHY framework, doesn't need to get other resource */
+       if (ep->using_phy)
+               return 0;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        ep->mem_res->phy_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(ep->mem_res->phy_base))
@@ -521,23 +521,25 @@ static void exynos_pcie_enable_interrupts(struct exynos_pcie *ep)
                exynos_pcie_msi_init(ep);
 }
 
-static u32 exynos_pcie_readl_dbi(struct dw_pcie *pci, u32 reg)
+static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
+                               u32 reg, size_t size)
 {
        struct exynos_pcie *ep = to_exynos_pcie(pci);
        u32 val;
 
        exynos_pcie_sideband_dbi_r_mode(ep, true);
-       val = readl(pci->dbi_base + reg);
+       dw_pcie_read(base + reg, size, &val);
        exynos_pcie_sideband_dbi_r_mode(ep, false);
        return val;
 }
 
-static void exynos_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
+static void exynos_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base,
+                                 u32 reg, size_t size, u32 val)
 {
        struct exynos_pcie *ep = to_exynos_pcie(pci);
 
        exynos_pcie_sideband_dbi_w_mode(ep, true);
-       writel(val, pci->dbi_base + reg);
+       dw_pcie_write(base + reg, size, val);
        exynos_pcie_sideband_dbi_w_mode(ep, false);
 }
 
@@ -644,8 +646,8 @@ static int __init exynos_add_pcie_port(struct exynos_pcie *ep,
 }
 
 static const struct dw_pcie_ops dw_pcie_ops = {
-       .readl_dbi = exynos_pcie_readl_dbi,
-       .writel_dbi = exynos_pcie_writel_dbi,
+       .read_dbi = exynos_pcie_read_dbi,
+       .write_dbi = exynos_pcie_write_dbi,
        .link_up = exynos_pcie_link_up,
 };
 
index 801e46cd266d79463e9b1a96fb418769c3414548..129717ae50225e3712bb8aea57686812b2b1105e 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/mfd/syscon.h>
 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <linux/mfd/syscon/imx7-iomuxc-gpr.h>
 #include <linux/module.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
@@ -27,6 +28,7 @@
 #include <linux/signal.h>
 #include <linux/types.h>
 #include <linux/interrupt.h>
+#include <linux/reset.h>
 
 #include "pcie-designware.h"
 
@@ -36,6 +38,7 @@ enum imx6_pcie_variants {
        IMX6Q,
        IMX6SX,
        IMX6QP,
+       IMX7D,
 };
 
 struct imx6_pcie {
@@ -47,6 +50,8 @@ struct imx6_pcie {
        struct clk              *pcie_inbound_axi;
        struct clk              *pcie;
        struct regmap           *iomuxc_gpr;
+       struct reset_control    *pciephy_reset;
+       struct reset_control    *apps_reset;
        enum imx6_pcie_variants variant;
        u32                     tx_deemph_gen1;
        u32                     tx_deemph_gen2_3p5db;
@@ -56,6 +61,11 @@ struct imx6_pcie {
        int                     link_gen;
 };
 
+/* Parameters for the waiting for PCIe PHY PLL to lock on i.MX7 */
+#define PHY_PLL_LOCK_WAIT_MAX_RETRIES  2000
+#define PHY_PLL_LOCK_WAIT_USLEEP_MIN   50
+#define PHY_PLL_LOCK_WAIT_USLEEP_MAX   200
+
 /* PCIe Root Complex registers (memory-mapped) */
 #define PCIE_RC_LCR                            0x7c
 #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1       0x1
@@ -248,6 +258,10 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
 static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
 {
        switch (imx6_pcie->variant) {
+       case IMX7D:
+               reset_control_assert(imx6_pcie->pciephy_reset);
+               reset_control_assert(imx6_pcie->apps_reset);
+               break;
        case IMX6SX:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
@@ -303,11 +317,32 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
                                   IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
                break;
+       case IMX7D:
+               break;
        }
 
        return ret;
 }
 
+static void imx7d_pcie_wait_for_phy_pll_lock(struct imx6_pcie *imx6_pcie)
+{
+       u32 val;
+       unsigned int retries;
+       struct device *dev = imx6_pcie->pci->dev;
+
+       for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES; retries++) {
+               regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR22, &val);
+
+               if (val & IMX7D_GPR22_PCIE_PHY_PLL_LOCKED)
+                       return;
+
+               usleep_range(PHY_PLL_LOCK_WAIT_USLEEP_MIN,
+                            PHY_PLL_LOCK_WAIT_USLEEP_MAX);
+       }
+
+       dev_err(dev, "PCIe PLL lock timeout\n");
+}
+
 static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
 {
        struct dw_pcie *pci = imx6_pcie->pci;
@@ -351,6 +386,10 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
        }
 
        switch (imx6_pcie->variant) {
+       case IMX7D:
+               reset_control_deassert(imx6_pcie->pciephy_reset);
+               imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie);
+               break;
        case IMX6SX:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
                                   IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
@@ -377,35 +416,44 @@ err_pcie_bus:
 
 static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
-       if (imx6_pcie->variant == IMX6SX)
+       switch (imx6_pcie->variant) {
+       case IMX7D:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX7D_GPR12_PCIE_PHY_REFCLK_SEL, 0);
+               break;
+       case IMX6SX:
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX6SX_GPR12_PCIE_RX_EQ_MASK,
                                   IMX6SX_GPR12_PCIE_RX_EQ_2);
+               /* FALLTHROUGH */
+       default:
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
 
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
+               /* configure constant input signal to the pcie ctrl and phy */
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
+
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_DEEMPH_GEN1,
+                                  imx6_pcie->tx_deemph_gen1 << 0);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
+                                  imx6_pcie->tx_deemph_gen2_3p5db << 6);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
+                                  imx6_pcie->tx_deemph_gen2_6db << 12);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_SWING_FULL,
+                                  imx6_pcie->tx_swing_full << 18);
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
+                                  IMX6Q_GPR8_TX_SWING_LOW,
+                                  imx6_pcie->tx_swing_low << 25);
+               break;
+       }
 
-       /* configure constant input signal to the pcie ctrl and phy */
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                        IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
-
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_DEEMPH_GEN1,
-                          imx6_pcie->tx_deemph_gen1 << 0);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB,
-                          imx6_pcie->tx_deemph_gen2_3p5db << 6);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB,
-                          imx6_pcie->tx_deemph_gen2_6db << 12);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_SWING_FULL,
-                          imx6_pcie->tx_swing_full << 18);
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
-                          IMX6Q_GPR8_TX_SWING_LOW,
-                          imx6_pcie->tx_swing_low << 25);
 }
 
 static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
@@ -469,8 +517,11 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
        dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
 
        /* Start LTSSM. */
-       regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
-                       IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
+       if (imx6_pcie->variant == IMX7D)
+               reset_control_deassert(imx6_pcie->apps_reset);
+       else
+               regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
+                                  IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 
        ret = imx6_pcie_wait_for_link(imx6_pcie);
        if (ret)
@@ -482,29 +533,40 @@ static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
                tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
                tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
                dw_pcie_writel_dbi(pci, PCIE_RC_LCR, tmp);
-       } else {
-               dev_info(dev, "Link: Gen2 disabled\n");
-       }
-
-       /*
-        * Start Directed Speed Change so the best possible speed both link
-        * partners support can be negotiated.
-        */
-       tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
-       tmp |= PORT_LOGIC_SPEED_CHANGE;
-       dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
 
-       ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
-       if (ret) {
-               dev_err(dev, "Failed to bring link up!\n");
-               goto err_reset_phy;
-       }
+               /*
+                * Start Directed Speed Change so the best possible
+                * speed both link partners support can be negotiated.
+                */
+               tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+               tmp |= PORT_LOGIC_SPEED_CHANGE;
+               dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
+
+               if (imx6_pcie->variant != IMX7D) {
+                       /*
+                        * On i.MX7, DIRECT_SPEED_CHANGE behaves differently
+                        * from i.MX6 family when no link speed transition
+                        * occurs and we go Gen1 -> yep, Gen1. The difference
+                        * is that, in such case, it will not be cleared by HW
+                        * which will cause the following code to report false
+                        * failure.
+                        */
+
+                       ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
+                       if (ret) {
+                               dev_err(dev, "Failed to bring link up!\n");
+                               goto err_reset_phy;
+                       }
+               }
 
-       /* Make sure link training is finished as well! */
-       ret = imx6_pcie_wait_for_link(imx6_pcie);
-       if (ret) {
-               dev_err(dev, "Failed to bring link up!\n");
-               goto err_reset_phy;
+               /* Make sure link training is finished as well! */
+               ret = imx6_pcie_wait_for_link(imx6_pcie);
+               if (ret) {
+                       dev_err(dev, "Failed to bring link up!\n");
+                       goto err_reset_phy;
+               }
+       } else {
+               dev_info(dev, "Link: Gen2 disabled\n");
        }
 
        tmp = dw_pcie_readl_dbi(pci, PCIE_RC_LCSR);
@@ -544,8 +606,8 @@ static struct dw_pcie_host_ops imx6_pcie_host_ops = {
        .host_init = imx6_pcie_host_init,
 };
 
-static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
-                                    struct platform_device *pdev)
+static int imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
+                             struct platform_device *pdev)
 {
        struct dw_pcie *pci = imx6_pcie->pci;
        struct pcie_port *pp = &pci->pp;
@@ -585,7 +647,7 @@ static const struct dw_pcie_ops dw_pcie_ops = {
        .link_up = imx6_pcie_link_up,
 };
 
-static int __init imx6_pcie_probe(struct platform_device *pdev)
+static int imx6_pcie_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct dw_pcie *pci;
@@ -609,10 +671,6 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
        imx6_pcie->variant =
                (enum imx6_pcie_variants)of_device_get_match_data(dev);
 
-       /* Added for PCI abort handling */
-       hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
-               "imprecise external abort");
-
        dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        pci->dbi_base = devm_ioremap_resource(dev, dbi_base);
        if (IS_ERR(pci->dbi_base))
@@ -632,6 +690,8 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                        dev_err(dev, "unable to get reset gpio\n");
                        return ret;
                }
+       } else if (imx6_pcie->reset_gpio == -EPROBE_DEFER) {
+               return imx6_pcie->reset_gpio;
        }
 
        /* Fetch clocks */
@@ -653,13 +713,31 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(imx6_pcie->pcie);
        }
 
-       if (imx6_pcie->variant == IMX6SX) {
+       switch (imx6_pcie->variant) {
+       case IMX6SX:
                imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
                                                           "pcie_inbound_axi");
                if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
                        dev_err(dev, "pcie_inbound_axi clock missing or invalid\n");
                        return PTR_ERR(imx6_pcie->pcie_inbound_axi);
                }
+               break;
+       case IMX7D:
+               imx6_pcie->pciephy_reset = devm_reset_control_get(dev,
+                                                                 "pciephy");
+               if (IS_ERR(imx6_pcie->pciephy_reset)) {
+                       dev_err(dev, "Failed to get PCIEPHY reset control\n");
+                       return PTR_ERR(imx6_pcie->pciephy_reset);
+               }
+
+               imx6_pcie->apps_reset = devm_reset_control_get(dev, "apps");
+               if (IS_ERR(imx6_pcie->apps_reset)) {
+                       dev_err(dev, "Failed to get PCIE APPS reset control\n");
+                       return PTR_ERR(imx6_pcie->apps_reset);
+               }
+               break;
+       default:
+               break;
        }
 
        /* Grab GPR config register range */
@@ -718,6 +796,7 @@ static const struct of_device_id imx6_pcie_of_match[] = {
        { .compatible = "fsl,imx6q-pcie",  .data = (void *)IMX6Q,  },
        { .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
        { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
+       { .compatible = "fsl,imx7d-pcie",  .data = (void *)IMX7D,  },
        {},
 };
 
@@ -726,11 +805,22 @@ static struct platform_driver imx6_pcie_driver = {
                .name   = "imx6q-pcie",
                .of_match_table = imx6_pcie_of_match,
        },
+       .probe    = imx6_pcie_probe,
        .shutdown = imx6_pcie_shutdown,
 };
 
 static int __init imx6_pcie_init(void)
 {
-       return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
+       /*
+        * Since probe() can be deferred we need to make sure that
+        * hook_fault_code is not called after __init memory is freed
+        * by kernel and since imx6q_pcie_abort_handler() is a no-op,
+        * we can install the handler here without risking it
+        * accessing some uninitialized driver state.
+        */
+       hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
+                       "imprecise external abort");
+
+       return platform_driver_register(&imx6_pcie_driver);
 }
 device_initcall(imx6_pcie_init);
index fcd3ef845883555648e0b2eeda907427d7811144..5b3b3afc0edbdd4c196e114585bed277f0f2c6c4 100644 (file)
@@ -78,6 +78,11 @@ static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u
        regmap_write(artpec6_pcie->regmap, offset, val);
 }
 
+static u64 artpec6_pcie_cpu_addr_fixup(u64 pci_addr)
+{
+       return pci_addr & ARTPEC6_CPU_TO_BUS_ADDR;
+}
+
 static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
 {
        struct dw_pcie *pci = artpec6_pcie->pci;
@@ -142,11 +147,6 @@ static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
         */
        dw_pcie_writel_dbi(pci, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
 
-       pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR;
-       pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR;
-       pp->cfg0_base &= ARTPEC6_CPU_TO_BUS_ADDR;
-       pp->cfg1_base &= ARTPEC6_CPU_TO_BUS_ADDR;
-
        /* setup root complex */
        dw_pcie_setup_rc(pp);
 
@@ -234,6 +234,10 @@ static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
        return 0;
 }
 
+static const struct dw_pcie_ops dw_pcie_ops = {
+       .cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup,
+};
+
 static int artpec6_pcie_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -252,6 +256,7 @@ static int artpec6_pcie_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        pci->dev = dev;
+       pci->ops = &dw_pcie_ops;
 
        artpec6_pcie->pci = pci;
 
diff --git a/drivers/pci/dwc/pcie-designware-ep.c b/drivers/pci/dwc/pcie-designware-ep.c
new file mode 100644 (file)
index 0000000..3984063
--- /dev/null
@@ -0,0 +1,342 @@
+/**
+ * Synopsys Designware PCIe Endpoint controller driver
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of.h>
+
+#include "pcie-designware.h"
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+
+void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
+{
+       struct pci_epc *epc = ep->epc;
+
+       pci_epc_linkup(epc);
+}
+
+static void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
+{
+       u32 reg;
+
+       reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+       dw_pcie_writel_dbi2(pci, reg, 0x0);
+       dw_pcie_writel_dbi(pci, reg, 0x0);
+}
+
+static int dw_pcie_ep_write_header(struct pci_epc *epc,
+                                  struct pci_epf_header *hdr)
+{
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, hdr->vendorid);
+       dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, hdr->deviceid);
+       dw_pcie_writeb_dbi(pci, PCI_REVISION_ID, hdr->revid);
+       dw_pcie_writeb_dbi(pci, PCI_CLASS_PROG, hdr->progif_code);
+       dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE,
+                          hdr->subclass_code | hdr->baseclass_code << 8);
+       dw_pcie_writeb_dbi(pci, PCI_CACHE_LINE_SIZE,
+                          hdr->cache_line_size);
+       dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_VENDOR_ID,
+                          hdr->subsys_vendor_id);
+       dw_pcie_writew_dbi(pci, PCI_SUBSYSTEM_ID, hdr->subsys_id);
+       dw_pcie_writeb_dbi(pci, PCI_INTERRUPT_PIN,
+                          hdr->interrupt_pin);
+
+       return 0;
+}
+
+static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, enum pci_barno bar,
+                                 dma_addr_t cpu_addr,
+                                 enum dw_pcie_as_type as_type)
+{
+       int ret;
+       u32 free_win;
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       free_win = find_first_zero_bit(&ep->ib_window_map,
+                                      sizeof(ep->ib_window_map));
+       if (free_win >= ep->num_ib_windows) {
+               dev_err(pci->dev, "no free inbound window\n");
+               return -EINVAL;
+       }
+
+       ret = dw_pcie_prog_inbound_atu(pci, free_win, bar, cpu_addr,
+                                      as_type);
+       if (ret < 0) {
+               dev_err(pci->dev, "Failed to program IB window\n");
+               return ret;
+       }
+
+       ep->bar_to_atu[bar] = free_win;
+       set_bit(free_win, &ep->ib_window_map);
+
+       return 0;
+}
+
+static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep, phys_addr_t phys_addr,
+                                  u64 pci_addr, size_t size)
+{
+       u32 free_win;
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       free_win = find_first_zero_bit(&ep->ob_window_map,
+                                      sizeof(ep->ob_window_map));
+       if (free_win >= ep->num_ob_windows) {
+               dev_err(pci->dev, "no free outbound window\n");
+               return -EINVAL;
+       }
+
+       dw_pcie_prog_outbound_atu(pci, free_win, PCIE_ATU_TYPE_MEM,
+                                 phys_addr, pci_addr, size);
+
+       set_bit(free_win, &ep->ob_window_map);
+       ep->outbound_addr[free_win] = phys_addr;
+
+       return 0;
+}
+
+static void dw_pcie_ep_clear_bar(struct pci_epc *epc, enum pci_barno bar)
+{
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       u32 atu_index = ep->bar_to_atu[bar];
+
+       dw_pcie_ep_reset_bar(pci, bar);
+
+       dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_INBOUND);
+       clear_bit(atu_index, &ep->ib_window_map);
+}
+
+static int dw_pcie_ep_set_bar(struct pci_epc *epc, enum pci_barno bar,
+                             dma_addr_t bar_phys, size_t size, int flags)
+{
+       int ret;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       enum dw_pcie_as_type as_type;
+       u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+
+       if (!(flags & PCI_BASE_ADDRESS_SPACE))
+               as_type = DW_PCIE_AS_MEM;
+       else
+               as_type = DW_PCIE_AS_IO;
+
+       ret = dw_pcie_ep_inbound_atu(ep, bar, bar_phys, as_type);
+       if (ret)
+               return ret;
+
+       dw_pcie_writel_dbi2(pci, reg, size - 1);
+       dw_pcie_writel_dbi(pci, reg, flags);
+
+       return 0;
+}
+
+static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
+                             u32 *atu_index)
+{
+       u32 index;
+
+       for (index = 0; index < ep->num_ob_windows; index++) {
+               if (ep->outbound_addr[index] != addr)
+                       continue;
+               *atu_index = index;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, phys_addr_t addr)
+{
+       int ret;
+       u32 atu_index;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       ret = dw_pcie_find_index(ep, addr, &atu_index);
+       if (ret < 0)
+               return;
+
+       dw_pcie_disable_atu(pci, atu_index, DW_PCIE_REGION_OUTBOUND);
+       clear_bit(atu_index, &ep->ob_window_map);
+}
+
+static int dw_pcie_ep_map_addr(struct pci_epc *epc, phys_addr_t addr,
+                              u64 pci_addr, size_t size)
+{
+       int ret;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       ret = dw_pcie_ep_outbound_atu(ep, addr, pci_addr, size);
+       if (ret) {
+               dev_err(pci->dev, "failed to enable address\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int dw_pcie_ep_get_msi(struct pci_epc *epc)
+{
+       int val;
+       u32 lower_addr;
+       u32 upper_addr;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       val = dw_pcie_readb_dbi(pci, MSI_MESSAGE_CONTROL);
+       val = (val & MSI_CAP_MME_MASK) >> MSI_CAP_MME_SHIFT;
+
+       lower_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_L32);
+       upper_addr = dw_pcie_readl_dbi(pci, MSI_MESSAGE_ADDR_U32);
+
+       if (!(lower_addr || upper_addr))
+               return -EINVAL;
+
+       return val;
+}
+
+static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 encode_int)
+{
+       int val;
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       val = (encode_int << MSI_CAP_MMC_SHIFT);
+       dw_pcie_writew_dbi(pci, MSI_MESSAGE_CONTROL, val);
+
+       return 0;
+}
+
+static int dw_pcie_ep_raise_irq(struct pci_epc *epc,
+                               enum pci_epc_irq_type type, u8 interrupt_num)
+{
+       struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+
+       if (!ep->ops->raise_irq)
+               return -EINVAL;
+
+       return ep->ops->raise_irq(ep, type, interrupt_num);
+}
+
+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);
+}
+
+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)
+               return -EINVAL;
+
+       return pci->ops->start_link(pci);
+}
+
+static const struct pci_epc_ops epc_ops = {
+       .write_header           = dw_pcie_ep_write_header,
+       .set_bar                = dw_pcie_ep_set_bar,
+       .clear_bar              = dw_pcie_ep_clear_bar,
+       .map_addr               = dw_pcie_ep_map_addr,
+       .unmap_addr             = dw_pcie_ep_unmap_addr,
+       .set_msi                = dw_pcie_ep_set_msi,
+       .get_msi                = dw_pcie_ep_get_msi,
+       .raise_irq              = dw_pcie_ep_raise_irq,
+       .start                  = dw_pcie_ep_start,
+       .stop                   = dw_pcie_ep_stop,
+};
+
+void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
+{
+       struct pci_epc *epc = ep->epc;
+
+       pci_epc_mem_exit(epc);
+}
+
+int dw_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+       int ret;
+       void *addr;
+       enum pci_barno bar;
+       struct pci_epc *epc;
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       struct device *dev = pci->dev;
+       struct device_node *np = dev->of_node;
+
+       if (!pci->dbi_base || !pci->dbi_base2) {
+               dev_err(dev, "dbi_base/deb_base2 is not populated\n");
+               return -EINVAL;
+       }
+
+       ret = of_property_read_u32(np, "num-ib-windows", &ep->num_ib_windows);
+       if (ret < 0) {
+               dev_err(dev, "unable to read *num-ib-windows* property\n");
+               return ret;
+       }
+
+       ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
+       if (ret < 0) {
+               dev_err(dev, "unable to read *num-ob-windows* property\n");
+               return ret;
+       }
+
+       addr = devm_kzalloc(dev, sizeof(phys_addr_t) * ep->num_ob_windows,
+                           GFP_KERNEL);
+       if (!addr)
+               return -ENOMEM;
+       ep->outbound_addr = addr;
+
+       for (bar = BAR_0; bar <= BAR_5; bar++)
+               dw_pcie_ep_reset_bar(pci, bar);
+
+       if (ep->ops->ep_init)
+               ep->ops->ep_init(ep);
+
+       epc = devm_pci_epc_create(dev, &epc_ops);
+       if (IS_ERR(epc)) {
+               dev_err(dev, "failed to create epc device\n");
+               return PTR_ERR(epc);
+       }
+
+       ret = of_property_read_u8(np, "max-functions", &epc->max_functions);
+       if (ret < 0)
+               epc->max_functions = 1;
+
+       ret = pci_epc_mem_init(epc, ep->phys_base, ep->addr_size);
+       if (ret < 0) {
+               dev_err(dev, "Failed to initialize address space\n");
+               return ret;
+       }
+
+       ep->epc = epc;
+       epc_set_drvdata(epc, ep);
+       dw_pcie_setup(pci);
+
+       return 0;
+}
index 5ba334938b52273917c160c829aae9baca267ebf..aece2962defc3c77a38bbda9b1397e849464c359 100644 (file)
@@ -56,24 +56,25 @@ static struct irq_chip dw_msi_irq_chip = {
 /* MSI int handler */
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
 {
-       unsigned long val;
+       u32 val;
        int i, pos, irq;
        irqreturn_t ret = IRQ_NONE;
 
        for (i = 0; i < MAX_MSI_CTRLS; i++) {
                dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
-                                   (u32 *)&val);
-               if (val) {
-                       ret = IRQ_HANDLED;
-                       pos = 0;
-                       while ((pos = find_next_bit(&val, 32, pos)) != 32) {
-                               irq = irq_find_mapping(pp->irq_domain,
-                                                      i * 32 + pos);
-                               dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS +
-                                                   i * 12, 4, 1 << pos);
-                               generic_handle_irq(irq);
-                               pos++;
-                       }
+                                   &val);
+               if (!val)
+                       continue;
+
+               ret = IRQ_HANDLED;
+               pos = 0;
+               while ((pos = find_next_bit((unsigned long *) &val, 32,
+                                           pos)) != 32) {
+                       irq = irq_find_mapping(pp->irq_domain, i * 32 + pos);
+                       dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12,
+                                           4, 1 << pos);
+                       generic_handle_irq(irq);
+                       pos++;
                }
        }
 
index b6c832ba39dd6905a4640e770b69aff47e51b738..f20d494922ab890badd99dd96af9bcad866f2ca2 100644 (file)
@@ -86,6 +86,9 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
        return 0;
 }
 
+static const struct dw_pcie_ops dw_pcie_ops = {
+};
+
 static int dw_plat_pcie_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -103,6 +106,7 @@ static int dw_plat_pcie_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        pci->dev = dev;
+       pci->ops = &dw_pcie_ops;
 
        dw_plat_pcie->pci = pci;
 
index 7e1fb7d6643cfe4c01e294220e4f8284f1b356e2..0e03af279259b735ade878bf96be3963270c1b15 100644 (file)
@@ -61,91 +61,253 @@ int dw_pcie_write(void __iomem *addr, int size, u32 val)
        return PCIBIOS_SUCCESSFUL;
 }
 
-u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg)
+u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+                      size_t size)
 {
-       if (pci->ops->readl_dbi)
-               return pci->ops->readl_dbi(pci, reg);
+       int ret;
+       u32 val;
 
-       return readl(pci->dbi_base + reg);
+       if (pci->ops->read_dbi)
+               return pci->ops->read_dbi(pci, base, reg, size);
+
+       ret = dw_pcie_read(base + reg, size, &val);
+       if (ret)
+               dev_err(pci->dev, "read DBI address failed\n");
+
+       return val;
 }
 
-void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
+void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+                        size_t size, u32 val)
 {
-       if (pci->ops->writel_dbi)
-               pci->ops->writel_dbi(pci, reg, val);
-       else
-               writel(val, pci->dbi_base + reg);
+       int ret;
+
+       if (pci->ops->write_dbi) {
+               pci->ops->write_dbi(pci, base, reg, size, val);
+               return;
+       }
+
+       ret = dw_pcie_write(base + reg, size, val);
+       if (ret)
+               dev_err(pci->dev, "write DBI address failed\n");
 }
 
-static u32 dw_pcie_readl_unroll(struct dw_pcie *pci, u32 index, u32 reg)
+static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg)
 {
        u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
 
        return dw_pcie_readl_dbi(pci, offset + reg);
 }
 
-static void dw_pcie_writel_unroll(struct dw_pcie *pci, u32 index, u32 reg,
-                                 u32 val)
+static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg,
+                                    u32 val)
 {
        u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
 
        dw_pcie_writel_dbi(pci, offset + reg, val);
 }
 
+void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, int type,
+                                     u64 cpu_addr, u64 pci_addr, u32 size)
+{
+       u32 retries, val;
+
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
+                                lower_32_bits(cpu_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
+                                upper_32_bits(cpu_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT,
+                                lower_32_bits(cpu_addr + size - 1));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
+                                lower_32_bits(pci_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
+                                upper_32_bits(pci_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
+                                type);
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
+                                PCIE_ATU_ENABLE);
+
+       /*
+        * Make sure ATU enable takes effect before any subsequent config
+        * and I/O accesses.
+        */
+       for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
+               val = dw_pcie_readl_ob_unroll(pci, index,
+                                             PCIE_ATU_UNR_REGION_CTRL2);
+               if (val & PCIE_ATU_ENABLE)
+                       return;
+
+               usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
+       }
+       dev_err(pci->dev, "outbound iATU is not being enabled\n");
+}
+
 void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
                               u64 cpu_addr, u64 pci_addr, u32 size)
 {
        u32 retries, val;
 
+       if (pci->ops->cpu_addr_fixup)
+               cpu_addr = pci->ops->cpu_addr_fixup(cpu_addr);
+
        if (pci->iatu_unroll_enabled) {
-               dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
-                                     lower_32_bits(cpu_addr));
-               dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
-                                     upper_32_bits(cpu_addr));
-               dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LIMIT,
-                                     lower_32_bits(cpu_addr + size - 1));
-               dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
-                                     lower_32_bits(pci_addr));
-               dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
-                                     upper_32_bits(pci_addr));
-               dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1,
-                                     type);
-               dw_pcie_writel_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
-                                     PCIE_ATU_ENABLE);
-       } else {
-               dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
-                                  PCIE_ATU_REGION_OUTBOUND | index);
-               dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE,
-                                  lower_32_bits(cpu_addr));
-               dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE,
-                                  upper_32_bits(cpu_addr));
-               dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
-                                  lower_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);
-               dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
+               dw_pcie_prog_outbound_atu_unroll(pci, index, type, cpu_addr,
+                                                pci_addr, size);
+               return;
        }
 
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
+                          PCIE_ATU_REGION_OUTBOUND | index);
+       dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE,
+                          lower_32_bits(cpu_addr));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE,
+                          upper_32_bits(cpu_addr));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
+                          lower_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);
+       dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
+
        /*
         * Make sure ATU enable takes effect before any subsequent config
         * and I/O accesses.
         */
        for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
-               if (pci->iatu_unroll_enabled)
-                       val = dw_pcie_readl_unroll(pci, index,
-                                                  PCIE_ATU_UNR_REGION_CTRL2);
-               else
-                       val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
-
+               val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
                if (val == PCIE_ATU_ENABLE)
                        return;
 
                usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
        }
-       dev_err(pci->dev, "iATU is not being enabled\n");
+       dev_err(pci->dev, "outbound iATU is not being enabled\n");
+}
+
+static u32 dw_pcie_readl_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg)
+{
+       u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index);
+
+       return dw_pcie_readl_dbi(pci, offset + reg);
+}
+
+static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg,
+                                    u32 val)
+{
+       u32 offset = PCIE_GET_ATU_INB_UNR_REG_OFFSET(index);
+
+       dw_pcie_writel_dbi(pci, offset + reg, val);
+}
+
+int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index, int bar,
+                                   u64 cpu_addr, enum dw_pcie_as_type as_type)
+{
+       int type;
+       u32 retries, val;
+
+       dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
+                                lower_32_bits(cpu_addr));
+       dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
+                                upper_32_bits(cpu_addr));
+
+       switch (as_type) {
+       case DW_PCIE_AS_MEM:
+               type = PCIE_ATU_TYPE_MEM;
+               break;
+       case DW_PCIE_AS_IO:
+               type = PCIE_ATU_TYPE_IO;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type);
+       dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
+                                PCIE_ATU_ENABLE |
+                                PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
+
+       /*
+        * Make sure ATU enable takes effect before any subsequent config
+        * and I/O accesses.
+        */
+       for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
+               val = dw_pcie_readl_ib_unroll(pci, index,
+                                             PCIE_ATU_UNR_REGION_CTRL2);
+               if (val & PCIE_ATU_ENABLE)
+                       return 0;
+
+               usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
+       }
+       dev_err(pci->dev, "inbound iATU is not being enabled\n");
+
+       return -EBUSY;
+}
+
+int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar,
+                            u64 cpu_addr, enum dw_pcie_as_type as_type)
+{
+       int type;
+       u32 retries, val;
+
+       if (pci->iatu_unroll_enabled)
+               return dw_pcie_prog_inbound_atu_unroll(pci, index, bar,
+                                                      cpu_addr, as_type);
+
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND |
+                          index);
+       dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr));
+
+       switch (as_type) {
+       case DW_PCIE_AS_MEM:
+               type = PCIE_ATU_TYPE_MEM;
+               break;
+       case DW_PCIE_AS_IO:
+               type = PCIE_ATU_TYPE_IO;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type);
+       dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE
+                          | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
+
+       /*
+        * Make sure ATU enable takes effect before any subsequent config
+        * and I/O accesses.
+        */
+       for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
+               val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2);
+               if (val & PCIE_ATU_ENABLE)
+                       return 0;
+
+               usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
+       }
+       dev_err(pci->dev, "inbound iATU is not being enabled\n");
+
+       return -EBUSY;
+}
+
+void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
+                        enum dw_pcie_region_type type)
+{
+       int region;
+
+       switch (type) {
+       case DW_PCIE_REGION_INBOUND:
+               region = PCIE_ATU_REGION_INBOUND;
+               break;
+       case DW_PCIE_REGION_OUTBOUND:
+               region = PCIE_ATU_REGION_OUTBOUND;
+               break;
+       default:
+               return;
+       }
+
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, region | index);
+       dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, ~PCIE_ATU_ENABLE);
 }
 
 int dw_pcie_wait_for_link(struct dw_pcie *pci)
index cd3b8713fe5044a7d3e4366f201972759ce3fcab..c6a840575796ff1bcc2617c4eb40f003668a3a4e 100644 (file)
@@ -18,6 +18,9 @@
 #include <linux/msi.h>
 #include <linux/pci.h>
 
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+
 /* Parameters for the waiting for link up routine */
 #define LINK_WAIT_MAX_RETRIES          10
 #define LINK_WAIT_USLEEP_MIN           90000
 #define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region)       \
                        ((0x3 << 20) | ((region) << 9))
 
+#define PCIE_GET_ATU_INB_UNR_REG_OFFSET(region)                                \
+                       ((0x3 << 20) | ((region) << 9) | (0x1 << 8))
+
+#define MSI_MESSAGE_CONTROL            0x52
+#define MSI_CAP_MMC_SHIFT              1
+#define MSI_CAP_MME_SHIFT              4
+#define MSI_CAP_MME_MASK               (7 << MSI_CAP_MME_SHIFT)
+#define MSI_MESSAGE_ADDR_L32           0x54
+#define MSI_MESSAGE_ADDR_U32           0x58
+
 /*
  * Maximum number of MSI IRQs can be 256 per controller. But keep
  * it 32 as of now. Probably we will never need more than 32. If needed,
 
 struct pcie_port;
 struct dw_pcie;
+struct dw_pcie_ep;
+
+enum dw_pcie_region_type {
+       DW_PCIE_REGION_UNKNOWN,
+       DW_PCIE_REGION_INBOUND,
+       DW_PCIE_REGION_OUTBOUND,
+};
+
+enum dw_pcie_device_mode {
+       DW_PCIE_UNKNOWN_TYPE,
+       DW_PCIE_EP_TYPE,
+       DW_PCIE_LEG_EP_TYPE,
+       DW_PCIE_RC_TYPE,
+};
 
 struct dw_pcie_host_ops {
        int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
@@ -142,35 +169,116 @@ struct pcie_port {
        DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
 };
 
+enum dw_pcie_as_type {
+       DW_PCIE_AS_UNKNOWN,
+       DW_PCIE_AS_MEM,
+       DW_PCIE_AS_IO,
+};
+
+struct dw_pcie_ep_ops {
+       void    (*ep_init)(struct dw_pcie_ep *ep);
+       int     (*raise_irq)(struct dw_pcie_ep *ep, enum pci_epc_irq_type type,
+                            u8 interrupt_num);
+};
+
+struct dw_pcie_ep {
+       struct pci_epc          *epc;
+       struct dw_pcie_ep_ops   *ops;
+       phys_addr_t             phys_base;
+       size_t                  addr_size;
+       u8                      bar_to_atu[6];
+       phys_addr_t             *outbound_addr;
+       unsigned long           ib_window_map;
+       unsigned long           ob_window_map;
+       u32                     num_ib_windows;
+       u32                     num_ob_windows;
+};
+
 struct dw_pcie_ops {
-       u32     (*readl_dbi)(struct dw_pcie *pcie, u32 reg);
-       void    (*writel_dbi)(struct dw_pcie *pcie, u32 reg, u32 val);
+       u64     (*cpu_addr_fixup)(u64 cpu_addr);
+       u32     (*read_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
+                           size_t size);
+       void    (*write_dbi)(struct dw_pcie *pcie, void __iomem *base, u32 reg,
+                            size_t size, u32 val);
        int     (*link_up)(struct dw_pcie *pcie);
+       int     (*start_link)(struct dw_pcie *pcie);
+       void    (*stop_link)(struct dw_pcie *pcie);
 };
 
 struct dw_pcie {
        struct device           *dev;
        void __iomem            *dbi_base;
+       void __iomem            *dbi_base2;
        u32                     num_viewport;
        u8                      iatu_unroll_enabled;
        struct pcie_port        pp;
+       struct dw_pcie_ep       ep;
        const struct dw_pcie_ops *ops;
 };
 
 #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
 
+#define to_dw_pcie_from_ep(endpoint)   \
+               container_of((endpoint), struct dw_pcie, ep)
+
 int dw_pcie_read(void __iomem *addr, int size, u32 *val);
 int dw_pcie_write(void __iomem *addr, int size, u32 val);
 
-u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg);
-void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val);
+u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+                      size_t size);
+void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg,
+                        size_t size, u32 val);
 int dw_pcie_link_up(struct dw_pcie *pci);
 int dw_pcie_wait_for_link(struct dw_pcie *pci);
 void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
                               int type, u64 cpu_addr, u64 pci_addr,
                               u32 size);
+int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int bar,
+                            u64 cpu_addr, enum dw_pcie_as_type as_type);
+void dw_pcie_disable_atu(struct dw_pcie *pci, int index,
+                        enum dw_pcie_region_type type);
 void dw_pcie_setup(struct dw_pcie *pci);
 
+static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val)
+{
+       __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x4, val);
+}
+
+static inline u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg)
+{
+       return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x4);
+}
+
+static inline void dw_pcie_writew_dbi(struct dw_pcie *pci, u32 reg, u16 val)
+{
+       __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x2, val);
+}
+
+static inline u16 dw_pcie_readw_dbi(struct dw_pcie *pci, u32 reg)
+{
+       return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x2);
+}
+
+static inline void dw_pcie_writeb_dbi(struct dw_pcie *pci, u32 reg, u8 val)
+{
+       __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x1, val);
+}
+
+static inline u8 dw_pcie_readb_dbi(struct dw_pcie *pci, u32 reg)
+{
+       return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x1);
+}
+
+static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val)
+{
+       __dw_pcie_write_dbi(pci, pci->dbi_base2, reg, 0x4, val);
+}
+
+static inline u32 dw_pcie_readl_dbi2(struct dw_pcie *pci, u32 reg)
+{
+       return __dw_pcie_read_dbi(pci, pci->dbi_base2, reg, 0x4);
+}
+
 #ifdef CONFIG_PCIE_DW_HOST
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
 void dw_pcie_msi_init(struct pcie_port *pp);
@@ -195,4 +303,23 @@ static inline int dw_pcie_host_init(struct pcie_port *pp)
        return 0;
 }
 #endif
+
+#ifdef CONFIG_PCIE_DW_EP
+void dw_pcie_ep_linkup(struct dw_pcie_ep *ep);
+int dw_pcie_ep_init(struct dw_pcie_ep *ep);
+void dw_pcie_ep_exit(struct dw_pcie_ep *ep);
+#else
+static inline void dw_pcie_ep_linkup(struct dw_pcie_ep *ep)
+{
+}
+
+static inline int dw_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+       return 0;
+}
+
+static inline void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
+{
+}
+#endif
 #endif /* _PCIE_DESIGNWARE_H */
diff --git a/drivers/pci/endpoint/Kconfig b/drivers/pci/endpoint/Kconfig
new file mode 100644 (file)
index 0000000..c23f146
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# PCI Endpoint Support
+#
+
+menu "PCI Endpoint"
+
+config PCI_ENDPOINT
+       bool "PCI Endpoint Support"
+       help
+          Enable this configuration option to support configurable PCI
+          endpoint. This should be enabled if the platform has a PCI
+          controller that can operate in endpoint mode.
+
+          Enabling this option will build the endpoint library, which
+          includes endpoint controller library and endpoint function
+          library.
+
+          If in doubt, say "N" to disable Endpoint support.
+
+config PCI_ENDPOINT_CONFIGFS
+       bool "PCI Endpoint Configfs Support"
+       depends on PCI_ENDPOINT
+       select CONFIGFS_FS
+       help
+          This will enable the configfs entry that can be used to
+          configure the endpoint function and used to bind the
+          function with a endpoint controller.
+
+source "drivers/pci/endpoint/functions/Kconfig"
+
+endmenu
diff --git a/drivers/pci/endpoint/Makefile b/drivers/pci/endpoint/Makefile
new file mode 100644 (file)
index 0000000..1041f80
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for PCI Endpoint Support
+#
+
+obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS)    += pci-ep-cfs.o
+obj-$(CONFIG_PCI_ENDPOINT)             += pci-epc-core.o pci-epf-core.o\
+                                          pci-epc-mem.o functions/
diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig
new file mode 100644 (file)
index 0000000..175edad
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# PCI Endpoint Functions
+#
+
+config PCI_EPF_TEST
+       tristate "PCI Endpoint Test driver"
+       depends on PCI_ENDPOINT
+       help
+          Enable this configuration option to enable the test driver
+          for PCI Endpoint.
+
+          If in doubt, say "N" to disable Endpoint test driver.
diff --git a/drivers/pci/endpoint/functions/Makefile b/drivers/pci/endpoint/functions/Makefile
new file mode 100644 (file)
index 0000000..6d94a48
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for PCI Endpoint Functions
+#
+
+obj-$(CONFIG_PCI_EPF_TEST)             += pci-epf-test.o
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
new file mode 100644 (file)
index 0000000..53fff80
--- /dev/null
@@ -0,0 +1,510 @@
+/**
+ * Test driver to test endpoint functionality
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+#include <linux/random.h>
+
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+#include <linux/pci_regs.h>
+
+#define COMMAND_RAISE_LEGACY_IRQ       BIT(0)
+#define COMMAND_RAISE_MSI_IRQ          BIT(1)
+#define MSI_NUMBER_SHIFT               2
+#define MSI_NUMBER_MASK                        (0x3f << MSI_NUMBER_SHIFT)
+#define COMMAND_READ                   BIT(8)
+#define COMMAND_WRITE                  BIT(9)
+#define COMMAND_COPY                   BIT(10)
+
+#define STATUS_READ_SUCCESS            BIT(0)
+#define STATUS_READ_FAIL               BIT(1)
+#define STATUS_WRITE_SUCCESS           BIT(2)
+#define STATUS_WRITE_FAIL              BIT(3)
+#define STATUS_COPY_SUCCESS            BIT(4)
+#define STATUS_COPY_FAIL               BIT(5)
+#define STATUS_IRQ_RAISED              BIT(6)
+#define STATUS_SRC_ADDR_INVALID                BIT(7)
+#define STATUS_DST_ADDR_INVALID                BIT(8)
+
+#define TIMER_RESOLUTION               1
+
+static struct workqueue_struct *kpcitest_workqueue;
+
+struct pci_epf_test {
+       void                    *reg[6];
+       struct pci_epf          *epf;
+       struct delayed_work     cmd_handler;
+};
+
+struct pci_epf_test_reg {
+       u32     magic;
+       u32     command;
+       u32     status;
+       u64     src_addr;
+       u64     dst_addr;
+       u32     size;
+       u32     checksum;
+} __packed;
+
+static struct pci_epf_header test_header = {
+       .vendorid       = PCI_ANY_ID,
+       .deviceid       = PCI_ANY_ID,
+       .baseclass_code = PCI_CLASS_OTHERS,
+       .interrupt_pin  = PCI_INTERRUPT_INTA,
+};
+
+static int bar_size[] = { 512, 1024, 16384, 131072, 1048576 };
+
+static int pci_epf_test_copy(struct pci_epf_test *epf_test)
+{
+       int ret;
+       void __iomem *src_addr;
+       void __iomem *dst_addr;
+       phys_addr_t src_phys_addr;
+       phys_addr_t dst_phys_addr;
+       struct pci_epf *epf = epf_test->epf;
+       struct device *dev = &epf->dev;
+       struct pci_epc *epc = epf->epc;
+       struct pci_epf_test_reg *reg = epf_test->reg[0];
+
+       src_addr = pci_epc_mem_alloc_addr(epc, &src_phys_addr, reg->size);
+       if (!src_addr) {
+               dev_err(dev, "failed to allocate source address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = pci_epc_map_addr(epc, src_phys_addr, reg->src_addr, reg->size);
+       if (ret) {
+               dev_err(dev, "failed to map source address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               goto err_src_addr;
+       }
+
+       dst_addr = pci_epc_mem_alloc_addr(epc, &dst_phys_addr, reg->size);
+       if (!dst_addr) {
+               dev_err(dev, "failed to allocate destination address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err_src_map_addr;
+       }
+
+       ret = pci_epc_map_addr(epc, dst_phys_addr, reg->dst_addr, reg->size);
+       if (ret) {
+               dev_err(dev, "failed to map destination address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               goto err_dst_addr;
+       }
+
+       memcpy(dst_addr, src_addr, reg->size);
+
+       pci_epc_unmap_addr(epc, dst_phys_addr);
+
+err_dst_addr:
+       pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size);
+
+err_src_map_addr:
+       pci_epc_unmap_addr(epc, src_phys_addr);
+
+err_src_addr:
+       pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size);
+
+err:
+       return ret;
+}
+
+static int pci_epf_test_read(struct pci_epf_test *epf_test)
+{
+       int ret;
+       void __iomem *src_addr;
+       void *buf;
+       u32 crc32;
+       phys_addr_t phys_addr;
+       struct pci_epf *epf = epf_test->epf;
+       struct device *dev = &epf->dev;
+       struct pci_epc *epc = epf->epc;
+       struct pci_epf_test_reg *reg = epf_test->reg[0];
+
+       src_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
+       if (!src_addr) {
+               dev_err(dev, "failed to allocate address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = pci_epc_map_addr(epc, phys_addr, reg->src_addr, reg->size);
+       if (ret) {
+               dev_err(dev, "failed to map address\n");
+               reg->status = STATUS_SRC_ADDR_INVALID;
+               goto err_addr;
+       }
+
+       buf = kzalloc(reg->size, GFP_KERNEL);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_map_addr;
+       }
+
+       memcpy(buf, src_addr, reg->size);
+
+       crc32 = crc32_le(~0, buf, reg->size);
+       if (crc32 != reg->checksum)
+               ret = -EIO;
+
+       kfree(buf);
+
+err_map_addr:
+       pci_epc_unmap_addr(epc, phys_addr);
+
+err_addr:
+       pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size);
+
+err:
+       return ret;
+}
+
+static int pci_epf_test_write(struct pci_epf_test *epf_test)
+{
+       int ret;
+       void __iomem *dst_addr;
+       void *buf;
+       phys_addr_t phys_addr;
+       struct pci_epf *epf = epf_test->epf;
+       struct device *dev = &epf->dev;
+       struct pci_epc *epc = epf->epc;
+       struct pci_epf_test_reg *reg = epf_test->reg[0];
+
+       dst_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
+       if (!dst_addr) {
+               dev_err(dev, "failed to allocate address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = pci_epc_map_addr(epc, phys_addr, reg->dst_addr, reg->size);
+       if (ret) {
+               dev_err(dev, "failed to map address\n");
+               reg->status = STATUS_DST_ADDR_INVALID;
+               goto err_addr;
+       }
+
+       buf = kzalloc(reg->size, GFP_KERNEL);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_map_addr;
+       }
+
+       get_random_bytes(buf, reg->size);
+       reg->checksum = crc32_le(~0, buf, reg->size);
+
+       memcpy(dst_addr, buf, reg->size);
+
+       /*
+        * wait 1ms inorder for the write to complete. Without this delay L3
+        * error in observed in the host system.
+        */
+       mdelay(1);
+
+       kfree(buf);
+
+err_map_addr:
+       pci_epc_unmap_addr(epc, phys_addr);
+
+err_addr:
+       pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size);
+
+err:
+       return ret;
+}
+
+static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test)
+{
+       u8 irq;
+       u8 msi_count;
+       struct pci_epf *epf = epf_test->epf;
+       struct pci_epc *epc = epf->epc;
+       struct pci_epf_test_reg *reg = epf_test->reg[0];
+
+       reg->status |= STATUS_IRQ_RAISED;
+       msi_count = pci_epc_get_msi(epc);
+       irq = (reg->command & MSI_NUMBER_MASK) >> MSI_NUMBER_SHIFT;
+       if (irq > msi_count || msi_count <= 0)
+               pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0);
+       else
+               pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq);
+}
+
+static void pci_epf_test_cmd_handler(struct work_struct *work)
+{
+       int ret;
+       u8 irq;
+       u8 msi_count;
+       struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test,
+                                                    cmd_handler.work);
+       struct pci_epf *epf = epf_test->epf;
+       struct pci_epc *epc = epf->epc;
+       struct pci_epf_test_reg *reg = epf_test->reg[0];
+
+       if (!reg->command)
+               goto reset_handler;
+
+       if (reg->command & COMMAND_RAISE_LEGACY_IRQ) {
+               reg->status = STATUS_IRQ_RAISED;
+               pci_epc_raise_irq(epc, PCI_EPC_IRQ_LEGACY, 0);
+               goto reset_handler;
+       }
+
+       if (reg->command & COMMAND_WRITE) {
+               ret = pci_epf_test_write(epf_test);
+               if (ret)
+                       reg->status |= STATUS_WRITE_FAIL;
+               else
+                       reg->status |= STATUS_WRITE_SUCCESS;
+               pci_epf_test_raise_irq(epf_test);
+               goto reset_handler;
+       }
+
+       if (reg->command & COMMAND_READ) {
+               ret = pci_epf_test_read(epf_test);
+               if (!ret)
+                       reg->status |= STATUS_READ_SUCCESS;
+               else
+                       reg->status |= STATUS_READ_FAIL;
+               pci_epf_test_raise_irq(epf_test);
+               goto reset_handler;
+       }
+
+       if (reg->command & COMMAND_COPY) {
+               ret = pci_epf_test_copy(epf_test);
+               if (!ret)
+                       reg->status |= STATUS_COPY_SUCCESS;
+               else
+                       reg->status |= STATUS_COPY_FAIL;
+               pci_epf_test_raise_irq(epf_test);
+               goto reset_handler;
+       }
+
+       if (reg->command & COMMAND_RAISE_MSI_IRQ) {
+               msi_count = pci_epc_get_msi(epc);
+               irq = (reg->command & MSI_NUMBER_MASK) >> MSI_NUMBER_SHIFT;
+               if (irq > msi_count || msi_count <= 0)
+                       goto reset_handler;
+               reg->status = STATUS_IRQ_RAISED;
+               pci_epc_raise_irq(epc, PCI_EPC_IRQ_MSI, irq);
+               goto reset_handler;
+       }
+
+reset_handler:
+       reg->command = 0;
+
+       queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler,
+                          msecs_to_jiffies(1));
+}
+
+static void pci_epf_test_linkup(struct pci_epf *epf)
+{
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+
+       queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler,
+                          msecs_to_jiffies(1));
+}
+
+static void pci_epf_test_unbind(struct pci_epf *epf)
+{
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+       struct pci_epc *epc = epf->epc;
+       int bar;
+
+       cancel_delayed_work(&epf_test->cmd_handler);
+       pci_epc_stop(epc);
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+               if (epf_test->reg[bar]) {
+                       pci_epf_free_space(epf, epf_test->reg[bar], bar);
+                       pci_epc_clear_bar(epc, bar);
+               }
+       }
+}
+
+static int pci_epf_test_set_bar(struct pci_epf *epf)
+{
+       int flags;
+       int bar;
+       int ret;
+       struct pci_epf_bar *epf_bar;
+       struct pci_epc *epc = epf->epc;
+       struct device *dev = &epf->dev;
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+
+       flags = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32;
+       if (sizeof(dma_addr_t) == 0x8)
+               flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
+
+       for (bar = BAR_0; bar <= BAR_5; bar++) {
+               epf_bar = &epf->bar[bar];
+               ret = pci_epc_set_bar(epc, bar, epf_bar->phys_addr,
+                                     epf_bar->size, flags);
+               if (ret) {
+                       pci_epf_free_space(epf, epf_test->reg[bar], bar);
+                       dev_err(dev, "failed to set BAR%d\n", bar);
+                       if (bar == BAR_0)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int pci_epf_test_alloc_space(struct pci_epf *epf)
+{
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+       struct device *dev = &epf->dev;
+       void *base;
+       int bar;
+
+       base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg),
+                                  BAR_0);
+       if (!base) {
+               dev_err(dev, "failed to allocated register space\n");
+               return -ENOMEM;
+       }
+       epf_test->reg[0] = base;
+
+       for (bar = BAR_1; bar <= BAR_5; bar++) {
+               base = pci_epf_alloc_space(epf, bar_size[bar - 1], bar);
+               if (!base)
+                       dev_err(dev, "failed to allocate space for BAR%d\n",
+                               bar);
+               epf_test->reg[bar] = base;
+       }
+
+       return 0;
+}
+
+static int pci_epf_test_bind(struct pci_epf *epf)
+{
+       int ret;
+       struct pci_epf_header *header = epf->header;
+       struct pci_epc *epc = epf->epc;
+       struct device *dev = &epf->dev;
+
+       if (WARN_ON_ONCE(!epc))
+               return -EINVAL;
+
+       ret = pci_epc_write_header(epc, header);
+       if (ret) {
+               dev_err(dev, "configuration header write failed\n");
+               return ret;
+       }
+
+       ret = pci_epf_test_alloc_space(epf);
+       if (ret)
+               return ret;
+
+       ret = pci_epf_test_set_bar(epf);
+       if (ret)
+               return ret;
+
+       ret = pci_epc_set_msi(epc, epf->msi_interrupts);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int pci_epf_test_probe(struct pci_epf *epf)
+{
+       struct pci_epf_test *epf_test;
+       struct device *dev = &epf->dev;
+
+       epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
+       if (!epf_test)
+               return -ENOMEM;
+
+       epf->header = &test_header;
+       epf_test->epf = epf;
+
+       INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
+
+       epf_set_drvdata(epf, epf_test);
+       return 0;
+}
+
+static int pci_epf_test_remove(struct pci_epf *epf)
+{
+       struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+
+       kfree(epf_test);
+       return 0;
+}
+
+static struct pci_epf_ops ops = {
+       .unbind = pci_epf_test_unbind,
+       .bind   = pci_epf_test_bind,
+       .linkup = pci_epf_test_linkup,
+};
+
+static const struct pci_epf_device_id pci_epf_test_ids[] = {
+       {
+               .name = "pci_epf_test",
+       },
+       {},
+};
+
+static struct pci_epf_driver test_driver = {
+       .driver.name    = "pci_epf_test",
+       .probe          = pci_epf_test_probe,
+       .remove         = pci_epf_test_remove,
+       .id_table       = pci_epf_test_ids,
+       .ops            = &ops,
+       .owner          = THIS_MODULE,
+};
+
+static int __init pci_epf_test_init(void)
+{
+       int ret;
+
+       kpcitest_workqueue = alloc_workqueue("kpcitest",
+                                            WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+       ret = pci_epf_register_driver(&test_driver);
+       if (ret) {
+               pr_err("failed to register pci epf test driver --> %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+module_init(pci_epf_test_init);
+
+static void __exit pci_epf_test_exit(void)
+{
+       pci_epf_unregister_driver(&test_driver);
+}
+module_exit(pci_epf_test_exit);
+
+MODULE_DESCRIPTION("PCI EPF TEST DRIVER");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c
new file mode 100644 (file)
index 0000000..424fdd6
--- /dev/null
@@ -0,0 +1,509 @@
+/**
+ * configfs to configure the PCI endpoint
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+#include <linux/pci-ep-cfs.h>
+
+static struct config_group *functions_group;
+static struct config_group *controllers_group;
+
+struct pci_epf_group {
+       struct config_group group;
+       struct pci_epf *epf;
+};
+
+struct pci_epc_group {
+       struct config_group group;
+       struct pci_epc *epc;
+       bool start;
+       unsigned long function_num_map;
+};
+
+static inline struct pci_epf_group *to_pci_epf_group(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct pci_epf_group, group);
+}
+
+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 ssize_t pci_epc_start_store(struct config_item *item, const char *page,
+                                  size_t len)
+{
+       int ret;
+       bool start;
+       struct pci_epc *epc;
+       struct pci_epc_group *epc_group = to_pci_epc_group(item);
+
+       epc = epc_group->epc;
+
+       ret = kstrtobool(page, &start);
+       if (ret)
+               return ret;
+
+       if (!start) {
+               pci_epc_stop(epc);
+               return len;
+       }
+
+       ret = pci_epc_start(epc);
+       if (ret) {
+               dev_err(&epc->dev, "failed to start endpoint controller\n");
+               return -EINVAL;
+       }
+
+       epc_group->start = start;
+
+       return len;
+}
+
+static ssize_t pci_epc_start_show(struct config_item *item, char *page)
+{
+       return sprintf(page, "%d\n",
+                      to_pci_epc_group(item)->start);
+}
+
+CONFIGFS_ATTR(pci_epc_, start);
+
+static struct configfs_attribute *pci_epc_attrs[] = {
+       &pci_epc_attr_start,
+       NULL,
+};
+
+static int pci_epc_epf_link(struct config_item *epc_item,
+                           struct config_item *epf_item)
+{
+       int ret;
+       u32 func_no = 0;
+       struct pci_epc *epc;
+       struct pci_epf *epf;
+       struct pci_epf_group *epf_group = to_pci_epf_group(epf_item);
+       struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
+
+       epc = epc_group->epc;
+       epf = epf_group->epf;
+       ret = pci_epc_add_epf(epc, epf);
+       if (ret)
+               goto err_add_epf;
+
+       func_no = find_first_zero_bit(&epc_group->function_num_map,
+                                     sizeof(epc_group->function_num_map));
+       set_bit(func_no, &epc_group->function_num_map);
+       epf->func_no = func_no;
+
+       ret = pci_epf_bind(epf);
+       if (ret)
+               goto err_epf_bind;
+
+       return 0;
+
+err_epf_bind:
+       pci_epc_remove_epf(epc, epf);
+
+err_add_epf:
+       clear_bit(func_no, &epc_group->function_num_map);
+
+       return ret;
+}
+
+static void pci_epc_epf_unlink(struct config_item *epc_item,
+                              struct config_item *epf_item)
+{
+       struct pci_epc *epc;
+       struct pci_epf *epf;
+       struct pci_epf_group *epf_group = to_pci_epf_group(epf_item);
+       struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
+
+       WARN_ON_ONCE(epc_group->start);
+
+       epc = epc_group->epc;
+       epf = epf_group->epf;
+       clear_bit(epf->func_no, &epc_group->function_num_map);
+       pci_epf_unbind(epf);
+       pci_epc_remove_epf(epc, epf);
+}
+
+static struct configfs_item_operations pci_epc_item_ops = {
+       .allow_link     = pci_epc_epf_link,
+       .drop_link      = pci_epc_epf_unlink,
+};
+
+static struct config_item_type pci_epc_type = {
+       .ct_item_ops    = &pci_epc_item_ops,
+       .ct_attrs       = pci_epc_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+struct config_group *pci_ep_cfs_add_epc_group(const char *name)
+{
+       int ret;
+       struct pci_epc *epc;
+       struct config_group *group;
+       struct pci_epc_group *epc_group;
+
+       epc_group = kzalloc(sizeof(*epc_group), GFP_KERNEL);
+       if (!epc_group) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       group = &epc_group->group;
+
+       config_group_init_type_name(group, name, &pci_epc_type);
+       ret = configfs_register_group(controllers_group, group);
+       if (ret) {
+               pr_err("failed to register configfs group for %s\n", name);
+               goto err_register_group;
+       }
+
+       epc = pci_epc_get(name);
+       if (IS_ERR(epc)) {
+               ret = PTR_ERR(epc);
+               goto err_epc_get;
+       }
+
+       epc_group->epc = epc;
+
+       return group;
+
+err_epc_get:
+       configfs_unregister_group(group);
+
+err_register_group:
+       kfree(epc_group);
+
+err:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(pci_ep_cfs_add_epc_group);
+
+void pci_ep_cfs_remove_epc_group(struct config_group *group)
+{
+       struct pci_epc_group *epc_group;
+
+       if (!group)
+               return;
+
+       epc_group = container_of(group, struct pci_epc_group, group);
+       pci_epc_put(epc_group->epc);
+       configfs_unregister_group(&epc_group->group);
+       kfree(epc_group);
+}
+EXPORT_SYMBOL(pci_ep_cfs_remove_epc_group);
+
+#define PCI_EPF_HEADER_R(_name)                                                       \
+static ssize_t pci_epf_##_name##_show(struct config_item *item,        char *page)    \
+{                                                                             \
+       struct pci_epf *epf = to_pci_epf_group(item)->epf;                     \
+       if (WARN_ON_ONCE(!epf->header))                                        \
+               return -EINVAL;                                                \
+       return sprintf(page, "0x%04x\n", epf->header->_name);                  \
+}
+
+#define PCI_EPF_HEADER_W_u32(_name)                                           \
+static ssize_t pci_epf_##_name##_store(struct config_item *item,              \
+                                      const char *page, size_t len)           \
+{                                                                             \
+       u32 val;                                                               \
+       int ret;                                                               \
+       struct pci_epf *epf = to_pci_epf_group(item)->epf;                     \
+       if (WARN_ON_ONCE(!epf->header))                                        \
+               return -EINVAL;                                                \
+       ret = kstrtou32(page, 0, &val);                                        \
+       if (ret)                                                               \
+               return ret;                                                    \
+       epf->header->_name = val;                                              \
+       return len;                                                            \
+}
+
+#define PCI_EPF_HEADER_W_u16(_name)                                           \
+static ssize_t pci_epf_##_name##_store(struct config_item *item,              \
+                                      const char *page, size_t len)           \
+{                                                                             \
+       u16 val;                                                               \
+       int ret;                                                               \
+       struct pci_epf *epf = to_pci_epf_group(item)->epf;                     \
+       if (WARN_ON_ONCE(!epf->header))                                        \
+               return -EINVAL;                                                \
+       ret = kstrtou16(page, 0, &val);                                        \
+       if (ret)                                                               \
+               return ret;                                                    \
+       epf->header->_name = val;                                              \
+       return len;                                                            \
+}
+
+#define PCI_EPF_HEADER_W_u8(_name)                                            \
+static ssize_t pci_epf_##_name##_store(struct config_item *item,              \
+                                      const char *page, size_t len)           \
+{                                                                             \
+       u8 val;                                                                \
+       int ret;                                                               \
+       struct pci_epf *epf = to_pci_epf_group(item)->epf;                     \
+       if (WARN_ON_ONCE(!epf->header))                                        \
+               return -EINVAL;                                                \
+       ret = kstrtou8(page, 0, &val);                                         \
+       if (ret)                                                               \
+               return ret;                                                    \
+       epf->header->_name = val;                                              \
+       return len;                                                            \
+}
+
+static ssize_t pci_epf_msi_interrupts_store(struct config_item *item,
+                                           const char *page, size_t len)
+{
+       u8 val;
+       int ret;
+
+       ret = kstrtou8(page, 0, &val);
+       if (ret)
+               return ret;
+
+       to_pci_epf_group(item)->epf->msi_interrupts = val;
+
+       return len;
+}
+
+static ssize_t pci_epf_msi_interrupts_show(struct config_item *item,
+                                          char *page)
+{
+       return sprintf(page, "%d\n",
+                      to_pci_epf_group(item)->epf->msi_interrupts);
+}
+
+PCI_EPF_HEADER_R(vendorid)
+PCI_EPF_HEADER_W_u16(vendorid)
+
+PCI_EPF_HEADER_R(deviceid)
+PCI_EPF_HEADER_W_u16(deviceid)
+
+PCI_EPF_HEADER_R(revid)
+PCI_EPF_HEADER_W_u8(revid)
+
+PCI_EPF_HEADER_R(progif_code)
+PCI_EPF_HEADER_W_u8(progif_code)
+
+PCI_EPF_HEADER_R(subclass_code)
+PCI_EPF_HEADER_W_u8(subclass_code)
+
+PCI_EPF_HEADER_R(baseclass_code)
+PCI_EPF_HEADER_W_u8(baseclass_code)
+
+PCI_EPF_HEADER_R(cache_line_size)
+PCI_EPF_HEADER_W_u8(cache_line_size)
+
+PCI_EPF_HEADER_R(subsys_vendor_id)
+PCI_EPF_HEADER_W_u16(subsys_vendor_id)
+
+PCI_EPF_HEADER_R(subsys_id)
+PCI_EPF_HEADER_W_u16(subsys_id)
+
+PCI_EPF_HEADER_R(interrupt_pin)
+PCI_EPF_HEADER_W_u8(interrupt_pin)
+
+CONFIGFS_ATTR(pci_epf_, vendorid);
+CONFIGFS_ATTR(pci_epf_, deviceid);
+CONFIGFS_ATTR(pci_epf_, revid);
+CONFIGFS_ATTR(pci_epf_, progif_code);
+CONFIGFS_ATTR(pci_epf_, subclass_code);
+CONFIGFS_ATTR(pci_epf_, baseclass_code);
+CONFIGFS_ATTR(pci_epf_, cache_line_size);
+CONFIGFS_ATTR(pci_epf_, subsys_vendor_id);
+CONFIGFS_ATTR(pci_epf_, subsys_id);
+CONFIGFS_ATTR(pci_epf_, interrupt_pin);
+CONFIGFS_ATTR(pci_epf_, msi_interrupts);
+
+static struct configfs_attribute *pci_epf_attrs[] = {
+       &pci_epf_attr_vendorid,
+       &pci_epf_attr_deviceid,
+       &pci_epf_attr_revid,
+       &pci_epf_attr_progif_code,
+       &pci_epf_attr_subclass_code,
+       &pci_epf_attr_baseclass_code,
+       &pci_epf_attr_cache_line_size,
+       &pci_epf_attr_subsys_vendor_id,
+       &pci_epf_attr_subsys_id,
+       &pci_epf_attr_interrupt_pin,
+       &pci_epf_attr_msi_interrupts,
+       NULL,
+};
+
+static void pci_epf_release(struct config_item *item)
+{
+       struct pci_epf_group *epf_group = to_pci_epf_group(item);
+
+       pci_epf_destroy(epf_group->epf);
+       kfree(epf_group);
+}
+
+static struct configfs_item_operations pci_epf_ops = {
+       .release                = pci_epf_release,
+};
+
+static struct config_item_type pci_epf_type = {
+       .ct_item_ops    = &pci_epf_ops,
+       .ct_attrs       = pci_epf_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_group *pci_epf_make(struct config_group *group,
+                                        const char *name)
+{
+       struct pci_epf_group *epf_group;
+       struct pci_epf *epf;
+
+       epf_group = kzalloc(sizeof(*epf_group), GFP_KERNEL);
+       if (!epf_group)
+               return ERR_PTR(-ENOMEM);
+
+       config_group_init_type_name(&epf_group->group, name, &pci_epf_type);
+
+       epf = pci_epf_create(group->cg_item.ci_name);
+       if (IS_ERR(epf)) {
+               pr_err("failed to create endpoint function device\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       epf_group->epf = epf;
+
+       return &epf_group->group;
+}
+
+static void pci_epf_drop(struct config_group *group, struct config_item *item)
+{
+       config_item_put(item);
+}
+
+static struct configfs_group_operations pci_epf_group_ops = {
+       .make_group     = &pci_epf_make,
+       .drop_item      = &pci_epf_drop,
+};
+
+static struct config_item_type pci_epf_group_type = {
+       .ct_group_ops   = &pci_epf_group_ops,
+       .ct_owner       = THIS_MODULE,
+};
+
+struct config_group *pci_ep_cfs_add_epf_group(const char *name)
+{
+       struct config_group *group;
+
+       group = configfs_register_default_group(functions_group, name,
+                                               &pci_epf_group_type);
+       if (IS_ERR(group))
+               pr_err("failed to register configfs group for %s function\n",
+                      name);
+
+       return group;
+}
+EXPORT_SYMBOL(pci_ep_cfs_add_epf_group);
+
+void pci_ep_cfs_remove_epf_group(struct config_group *group)
+{
+       if (IS_ERR_OR_NULL(group))
+               return;
+
+       configfs_unregister_default_group(group);
+}
+EXPORT_SYMBOL(pci_ep_cfs_remove_epf_group);
+
+static struct config_item_type pci_functions_type = {
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_item_type pci_controllers_type = {
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct config_item_type pci_ep_type = {
+       .ct_owner       = THIS_MODULE,
+};
+
+static struct configfs_subsystem pci_ep_cfs_subsys = {
+       .su_group = {
+               .cg_item = {
+                       .ci_namebuf = "pci_ep",
+                       .ci_type = &pci_ep_type,
+               },
+       },
+       .su_mutex = __MUTEX_INITIALIZER(pci_ep_cfs_subsys.su_mutex),
+};
+
+static int __init pci_ep_cfs_init(void)
+{
+       int ret;
+       struct config_group *root = &pci_ep_cfs_subsys.su_group;
+
+       config_group_init(root);
+
+       ret = configfs_register_subsystem(&pci_ep_cfs_subsys);
+       if (ret) {
+               pr_err("Error %d while registering subsystem %s\n",
+                      ret, root->cg_item.ci_namebuf);
+               goto err;
+       }
+
+       functions_group = configfs_register_default_group(root, "functions",
+                                                         &pci_functions_type);
+       if (IS_ERR(functions_group)) {
+               ret = PTR_ERR(functions_group);
+               pr_err("Error %d while registering functions group\n",
+                      ret);
+               goto err_functions_group;
+       }
+
+       controllers_group =
+               configfs_register_default_group(root, "controllers",
+                                               &pci_controllers_type);
+       if (IS_ERR(controllers_group)) {
+               ret = PTR_ERR(controllers_group);
+               pr_err("Error %d while registering controllers group\n",
+                      ret);
+               goto err_controllers_group;
+       }
+
+       return 0;
+
+err_controllers_group:
+       configfs_unregister_default_group(functions_group);
+
+err_functions_group:
+       configfs_unregister_subsystem(&pci_ep_cfs_subsys);
+
+err:
+       return ret;
+}
+module_init(pci_ep_cfs_init);
+
+static void __exit pci_ep_cfs_exit(void)
+{
+       configfs_unregister_default_group(controllers_group);
+       configfs_unregister_default_group(functions_group);
+       configfs_unregister_subsystem(&pci_ep_cfs_subsys);
+}
+module_exit(pci_ep_cfs_exit);
+
+MODULE_DESCRIPTION("PCI EP CONFIGFS");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
new file mode 100644 (file)
index 0000000..caa7be1
--- /dev/null
@@ -0,0 +1,580 @@
+/**
+ * PCI Endpoint *Controller* (EPC) library
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+#include <linux/pci-ep-cfs.h>
+
+static struct class *pci_epc_class;
+
+static void devm_pci_epc_release(struct device *dev, void *res)
+{
+       struct pci_epc *epc = *(struct pci_epc **)res;
+
+       pci_epc_destroy(epc);
+}
+
+static int devm_pci_epc_match(struct device *dev, void *res, void *match_data)
+{
+       struct pci_epc **epc = res;
+
+       return *epc == match_data;
+}
+
+/**
+ * pci_epc_put() - release the PCI endpoint controller
+ * @epc: epc returned by pci_epc_get()
+ *
+ * release the refcount the caller obtained by invoking pci_epc_get()
+ */
+void pci_epc_put(struct pci_epc *epc)
+{
+       if (!epc || IS_ERR(epc))
+               return;
+
+       module_put(epc->ops->owner);
+       put_device(&epc->dev);
+}
+EXPORT_SYMBOL_GPL(pci_epc_put);
+
+/**
+ * pci_epc_get() - get the PCI endpoint controller
+ * @epc_name: device name of the endpoint controller
+ *
+ * Invoke to get struct pci_epc * corresponding to the device name of the
+ * endpoint controller
+ */
+struct pci_epc *pci_epc_get(const char *epc_name)
+{
+       int ret = -EINVAL;
+       struct pci_epc *epc;
+       struct device *dev;
+       struct class_dev_iter iter;
+
+       class_dev_iter_init(&iter, pci_epc_class, NULL, NULL);
+       while ((dev = class_dev_iter_next(&iter))) {
+               if (strcmp(epc_name, dev_name(dev)))
+                       continue;
+
+               epc = to_pci_epc(dev);
+               if (!try_module_get(epc->ops->owner)) {
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               class_dev_iter_exit(&iter);
+               get_device(&epc->dev);
+               return epc;
+       }
+
+err:
+       class_dev_iter_exit(&iter);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(pci_epc_get);
+
+/**
+ * pci_epc_stop() - stop the PCI link
+ * @epc: the link of the EPC device that has to be stopped
+ *
+ * Invoke to stop the PCI link
+ */
+void pci_epc_stop(struct pci_epc *epc)
+{
+       unsigned long flags;
+
+       if (IS_ERR(epc) || !epc->ops->stop)
+               return;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       epc->ops->stop(epc);
+       spin_unlock_irqrestore(&epc->lock, flags);
+}
+EXPORT_SYMBOL_GPL(pci_epc_stop);
+
+/**
+ * pci_epc_start() - start the PCI link
+ * @epc: the link of *this* EPC device has to be started
+ *
+ * Invoke to start the PCI link
+ */
+int pci_epc_start(struct pci_epc *epc)
+{
+       int ret;
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return -EINVAL;
+
+       if (!epc->ops->start)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       ret = epc->ops->start(epc);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_start);
+
+/**
+ * pci_epc_raise_irq() - interrupt the host system
+ * @epc: the EPC device which has to interrupt the host
+ * @type: specify the type of interrupt; legacy or MSI
+ * @interrupt_num: the MSI interrupt number
+ *
+ * Invoke to raise an MSI or legacy interrupt
+ */
+int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type,
+                     u8 interrupt_num)
+{
+       int ret;
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return -EINVAL;
+
+       if (!epc->ops->raise_irq)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       ret = epc->ops->raise_irq(epc, type, interrupt_num);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_raise_irq);
+
+/**
+ * pci_epc_get_msi() - get the number of MSI interrupt numbers allocated
+ * @epc: the EPC device to which MSI interrupts was requested
+ *
+ * Invoke to get the number of MSI interrupts allocated by the RC
+ */
+int pci_epc_get_msi(struct pci_epc *epc)
+{
+       int interrupt;
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return 0;
+
+       if (!epc->ops->get_msi)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       interrupt = epc->ops->get_msi(epc);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       if (interrupt < 0)
+               return 0;
+
+       interrupt = 1 << interrupt;
+
+       return interrupt;
+}
+EXPORT_SYMBOL_GPL(pci_epc_get_msi);
+
+/**
+ * pci_epc_set_msi() - set the number of MSI interrupt numbers required
+ * @epc: the EPC device on which MSI has to be configured
+ * @interrupts: number of MSI interrupts required by the EPF
+ *
+ * Invoke to set the required number of MSI interrupts.
+ */
+int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts)
+{
+       int ret;
+       u8 encode_int;
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return -EINVAL;
+
+       if (!epc->ops->set_msi)
+               return 0;
+
+       encode_int = order_base_2(interrupts);
+
+       spin_lock_irqsave(&epc->lock, flags);
+       ret = epc->ops->set_msi(epc, encode_int);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_set_msi);
+
+/**
+ * pci_epc_unmap_addr() - unmap CPU address from PCI address
+ * @epc: the EPC device on which address is allocated
+ * @phys_addr: physical address of the local system
+ *
+ * Invoke to unmap the CPU address from PCI address.
+ */
+void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr)
+{
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return;
+
+       if (!epc->ops->unmap_addr)
+               return;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       epc->ops->unmap_addr(epc, phys_addr);
+       spin_unlock_irqrestore(&epc->lock, flags);
+}
+EXPORT_SYMBOL_GPL(pci_epc_unmap_addr);
+
+/**
+ * pci_epc_map_addr() - map CPU address to PCI address
+ * @epc: the EPC device on which address is allocated
+ * @phys_addr: physical address of the local system
+ * @pci_addr: PCI address to which the physical address should be mapped
+ * @size: the size of the allocation
+ *
+ * Invoke to map CPU address with PCI address.
+ */
+int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr,
+                    u64 pci_addr, size_t size)
+{
+       int ret;
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return -EINVAL;
+
+       if (!epc->ops->map_addr)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       ret = epc->ops->map_addr(epc, phys_addr, pci_addr, size);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_map_addr);
+
+/**
+ * pci_epc_clear_bar() - reset the BAR
+ * @epc: the EPC device for which the BAR has to be cleared
+ * @bar: the BAR number that has to be reset
+ *
+ * Invoke to reset the BAR of the endpoint device.
+ */
+void pci_epc_clear_bar(struct pci_epc *epc, int bar)
+{
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return;
+
+       if (!epc->ops->clear_bar)
+               return;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       epc->ops->clear_bar(epc, bar);
+       spin_unlock_irqrestore(&epc->lock, flags);
+}
+EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
+
+/**
+ * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space
+ * @epc: the EPC device on which BAR has to be configured
+ * @bar: the BAR number that has to be configured
+ * @size: the size of the addr space
+ * @flags: specify memory allocation/io allocation/32bit address/64 bit address
+ *
+ * Invoke to configure the BAR of the endpoint device.
+ */
+int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar,
+                   dma_addr_t bar_phys, size_t size, int flags)
+{
+       int ret;
+       unsigned long irq_flags;
+
+       if (IS_ERR(epc))
+               return -EINVAL;
+
+       if (!epc->ops->set_bar)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, irq_flags);
+       ret = epc->ops->set_bar(epc, bar, bar_phys, size, flags);
+       spin_unlock_irqrestore(&epc->lock, irq_flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_set_bar);
+
+/**
+ * pci_epc_write_header() - write standard configuration header
+ * @epc: the EPC device to which the configuration header should be written
+ * @header: standard configuration header fields
+ *
+ * Invoke to write the configuration header to the endpoint controller. Every
+ * endpoint controller will have a dedicated location to which the standard
+ * configuration header would be written. The callback function should write
+ * the header fields to this dedicated location.
+ */
+int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *header)
+{
+       int ret;
+       unsigned long flags;
+
+       if (IS_ERR(epc))
+               return -EINVAL;
+
+       if (!epc->ops->write_header)
+               return 0;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       ret = epc->ops->write_header(epc, header);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       return ret;
+}
+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
+ *
+ * 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)
+{
+       unsigned long flags;
+
+       if (epf->epc)
+               return -EBUSY;
+
+       if (IS_ERR(epc))
+               return -EINVAL;
+
+       if (epf->func_no > epc->max_functions - 1)
+               return -EINVAL;
+
+       epf->epc = epc;
+       dma_set_coherent_mask(&epf->dev, epc->dev.coherent_dma_mask);
+       epf->dev.dma_mask = epc->dev.dma_mask;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       list_add_tail(&epf->list, &epc->pci_epf);
+       spin_unlock_irqrestore(&epc->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epc_add_epf);
+
+/**
+ * pci_epc_remove_epf() - remove PCI endpoint function from endpoint controller
+ * @epc: the EPC device from which the endpoint function should be removed
+ * @epf: the endpoint function to be removed
+ *
+ * Invoke to remove PCI endpoint function from the endpoint controller.
+ */
+void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf)
+{
+       unsigned long flags;
+
+       if (!epc || IS_ERR(epc))
+               return;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       list_del(&epf->list);
+       spin_unlock_irqrestore(&epc->lock, flags);
+}
+EXPORT_SYMBOL_GPL(pci_epc_remove_epf);
+
+/**
+ * pci_epc_linkup() - Notify the EPF device that EPC device has established a
+ *                   connection with the Root Complex.
+ * @epc: the EPC device which has established link with the host
+ *
+ * Invoke to Notify the EPF device that the EPC device has established a
+ * connection with the Root Complex.
+ */
+void pci_epc_linkup(struct pci_epc *epc)
+{
+       unsigned long flags;
+       struct pci_epf *epf;
+
+       if (!epc || IS_ERR(epc))
+               return;
+
+       spin_lock_irqsave(&epc->lock, flags);
+       list_for_each_entry(epf, &epc->pci_epf, list)
+               pci_epf_linkup(epf);
+       spin_unlock_irqrestore(&epc->lock, flags);
+}
+EXPORT_SYMBOL_GPL(pci_epc_linkup);
+
+/**
+ * pci_epc_destroy() - destroy the EPC device
+ * @epc: the EPC device that has to be destroyed
+ *
+ * Invoke to destroy the PCI EPC device
+ */
+void pci_epc_destroy(struct pci_epc *epc)
+{
+       pci_ep_cfs_remove_epc_group(epc->group);
+       device_unregister(&epc->dev);
+       kfree(epc);
+}
+EXPORT_SYMBOL_GPL(pci_epc_destroy);
+
+/**
+ * devm_pci_epc_destroy() - destroy the EPC device
+ * @dev: device that wants to destroy the EPC
+ * @epc: the EPC device that has to be destroyed
+ *
+ * Invoke to destroy the devres associated with this
+ * pci_epc and destroy the EPC device.
+ */
+void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc)
+{
+       int r;
+
+       r = devres_destroy(dev, devm_pci_epc_release, devm_pci_epc_match,
+                          epc);
+       dev_WARN_ONCE(dev, r, "couldn't find PCI EPC resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_pci_epc_destroy);
+
+/**
+ * __pci_epc_create() - create a new endpoint controller (EPC) device
+ * @dev: device that is creating the new EPC
+ * @ops: function pointers for performing EPC operations
+ * @owner: the owner of the module that creates the EPC device
+ *
+ * Invoke to create a new EPC device and add it to pci_epc class.
+ */
+struct pci_epc *
+__pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
+                struct module *owner)
+{
+       int ret;
+       struct pci_epc *epc;
+
+       if (WARN_ON(!dev)) {
+               ret = -EINVAL;
+               goto err_ret;
+       }
+
+       epc = kzalloc(sizeof(*epc), GFP_KERNEL);
+       if (!epc) {
+               ret = -ENOMEM;
+               goto err_ret;
+       }
+
+       spin_lock_init(&epc->lock);
+       INIT_LIST_HEAD(&epc->pci_epf);
+
+       device_initialize(&epc->dev);
+       dma_set_coherent_mask(&epc->dev, dev->coherent_dma_mask);
+       epc->dev.class = pci_epc_class;
+       epc->dev.dma_mask = dev->dma_mask;
+       epc->ops = ops;
+
+       ret = dev_set_name(&epc->dev, "%s", dev_name(dev));
+       if (ret)
+               goto put_dev;
+
+       ret = device_add(&epc->dev);
+       if (ret)
+               goto put_dev;
+
+       epc->group = pci_ep_cfs_add_epc_group(dev_name(dev));
+
+       return epc;
+
+put_dev:
+       put_device(&epc->dev);
+       kfree(epc);
+
+err_ret:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(__pci_epc_create);
+
+/**
+ * __devm_pci_epc_create() - create a new endpoint controller (EPC) device
+ * @dev: device that is creating the new EPC
+ * @ops: function pointers for performing EPC operations
+ * @owner: the owner of the module that creates the EPC device
+ *
+ * Invoke to create a new EPC device and add it to pci_epc class.
+ * While at that, it also associates the device with the pci_epc using devres.
+ * On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct pci_epc *
+__devm_pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
+                     struct module *owner)
+{
+       struct pci_epc **ptr, *epc;
+
+       ptr = devres_alloc(devm_pci_epc_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       epc = __pci_epc_create(dev, ops, owner);
+       if (!IS_ERR(epc)) {
+               *ptr = epc;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return epc;
+}
+EXPORT_SYMBOL_GPL(__devm_pci_epc_create);
+
+static int __init pci_epc_init(void)
+{
+       pci_epc_class = class_create(THIS_MODULE, "pci_epc");
+       if (IS_ERR(pci_epc_class)) {
+               pr_err("failed to create pci epc class --> %ld\n",
+                      PTR_ERR(pci_epc_class));
+               return PTR_ERR(pci_epc_class);
+       }
+
+       return 0;
+}
+module_init(pci_epc_init);
+
+static void __exit pci_epc_exit(void)
+{
+       class_destroy(pci_epc_class);
+}
+module_exit(pci_epc_exit);
+
+MODULE_DESCRIPTION("PCI EPC Library");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/endpoint/pci-epc-mem.c b/drivers/pci/endpoint/pci-epc-mem.c
new file mode 100644 (file)
index 0000000..3a94cc1
--- /dev/null
@@ -0,0 +1,143 @@
+/**
+ * PCI Endpoint *Controller* Address Space Management
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/pci-epc.h>
+
+/**
+ * pci_epc_mem_init() - initialize the pci_epc_mem structure
+ * @epc: the EPC device that invoked pci_epc_mem_init
+ * @phys_base: the physical address of the base
+ * @size: the size of the address space
+ *
+ * Invoke to initialize the pci_epc_mem structure used by the
+ * endpoint functions to allocate mapped PCI address.
+ */
+int pci_epc_mem_init(struct pci_epc *epc, phys_addr_t phys_base, size_t size)
+{
+       int ret;
+       struct pci_epc_mem *mem;
+       unsigned long *bitmap;
+       int pages = size >> PAGE_SHIFT;
+       int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
+
+       mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+       if (!mem) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+       if (!bitmap) {
+               ret = -ENOMEM;
+               goto err_mem;
+       }
+
+       mem->bitmap = bitmap;
+       mem->phys_base = phys_base;
+       mem->pages = pages;
+       mem->size = size;
+
+       epc->mem = mem;
+
+       return 0;
+
+err_mem:
+       kfree(mem);
+
+err:
+return ret;
+}
+EXPORT_SYMBOL_GPL(pci_epc_mem_init);
+
+/**
+ * pci_epc_mem_exit() - cleanup the pci_epc_mem structure
+ * @epc: the EPC device that invoked pci_epc_mem_exit
+ *
+ * Invoke to cleanup the pci_epc_mem structure allocated in
+ * pci_epc_mem_init().
+ */
+void pci_epc_mem_exit(struct pci_epc *epc)
+{
+       struct pci_epc_mem *mem = epc->mem;
+
+       epc->mem = NULL;
+       kfree(mem->bitmap);
+       kfree(mem);
+}
+EXPORT_SYMBOL_GPL(pci_epc_mem_exit);
+
+/**
+ * pci_epc_mem_alloc_addr() - allocate memory address from EPC addr space
+ * @epc: the EPC device on which memory has to be allocated
+ * @phys_addr: populate the allocated physical address here
+ * @size: the size of the address space that has to be allocated
+ *
+ * Invoke to allocate memory address from the EPC address space. This
+ * is usually done to map the remote RC address into the local system.
+ */
+void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
+                                    phys_addr_t *phys_addr, size_t size)
+{
+       int pageno;
+       void __iomem *virt_addr;
+       struct pci_epc_mem *mem = epc->mem;
+       int order = get_order(size);
+
+       pageno = bitmap_find_free_region(mem->bitmap, mem->pages, order);
+       if (pageno < 0)
+               return NULL;
+
+       *phys_addr = mem->phys_base + (pageno << PAGE_SHIFT);
+       virt_addr = ioremap(*phys_addr, size);
+       if (!virt_addr)
+               bitmap_release_region(mem->bitmap, pageno, order);
+
+       return virt_addr;
+}
+EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr);
+
+/**
+ * pci_epc_mem_free_addr() - free the allocated memory address
+ * @epc: the EPC device on which memory was allocated
+ * @phys_addr: the allocated physical address
+ * @virt_addr: virtual address of the allocated mem space
+ * @size: the size of the allocated address space
+ *
+ * Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
+ */
+void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
+                          void __iomem *virt_addr, size_t size)
+{
+       int pageno;
+       int order = get_order(size);
+       struct pci_epc_mem *mem = epc->mem;
+
+       iounmap(virt_addr);
+       pageno = (phys_addr - mem->phys_base) >> PAGE_SHIFT;
+       bitmap_release_region(mem->bitmap, pageno, order);
+}
+EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr);
+
+MODULE_DESCRIPTION("PCI EPC Address Space Management");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c
new file mode 100644 (file)
index 0000000..6877d6a
--- /dev/null
@@ -0,0 +1,359 @@
+/**
+ * PCI Endpoint *Function* (EPF) library
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+#include <linux/pci-ep-cfs.h>
+
+static struct bus_type pci_epf_bus_type;
+static struct device_type pci_epf_type;
+
+/**
+ * pci_epf_linkup() - Notify the function driver that EPC device has
+ *                   established a connection with the Root Complex.
+ * @epf: the EPF device bound to the EPC device which has established
+ *      the connection with the host
+ *
+ * Invoke to notify the function driver that EPC device has established
+ * a connection with the Root Complex.
+ */
+void pci_epf_linkup(struct pci_epf *epf)
+{
+       if (!epf->driver) {
+               dev_WARN(&epf->dev, "epf device not bound to driver\n");
+               return;
+       }
+
+       epf->driver->ops->linkup(epf);
+}
+EXPORT_SYMBOL_GPL(pci_epf_linkup);
+
+/**
+ * pci_epf_unbind() - Notify the function driver that the binding between the
+ *                   EPF device and EPC device has been lost
+ * @epf: the EPF device which has lost the binding with the EPC device
+ *
+ * Invoke to notify the function driver that the binding between the EPF device
+ * and EPC device has been lost.
+ */
+void pci_epf_unbind(struct pci_epf *epf)
+{
+       if (!epf->driver) {
+               dev_WARN(&epf->dev, "epf device not bound to driver\n");
+               return;
+       }
+
+       epf->driver->ops->unbind(epf);
+       module_put(epf->driver->owner);
+}
+EXPORT_SYMBOL_GPL(pci_epf_unbind);
+
+/**
+ * pci_epf_bind() - Notify the function driver that the EPF device has been
+ *                 bound to a EPC device
+ * @epf: the EPF device which has been bound to the EPC device
+ *
+ * Invoke to notify the function driver that it has been bound to a EPC device
+ */
+int pci_epf_bind(struct pci_epf *epf)
+{
+       if (!epf->driver) {
+               dev_WARN(&epf->dev, "epf device not bound to driver\n");
+               return -EINVAL;
+       }
+
+       if (!try_module_get(epf->driver->owner))
+               return -EAGAIN;
+
+       return epf->driver->ops->bind(epf);
+}
+EXPORT_SYMBOL_GPL(pci_epf_bind);
+
+/**
+ * pci_epf_free_space() - free the allocated PCI EPF register space
+ * @addr: the virtual address of the PCI EPF register space
+ * @bar: the BAR number corresponding to the register space
+ *
+ * Invoke to free the allocated PCI EPF register space.
+ */
+void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar)
+{
+       struct device *dev = &epf->dev;
+
+       if (!addr)
+               return;
+
+       dma_free_coherent(dev, epf->bar[bar].size, addr,
+                         epf->bar[bar].phys_addr);
+
+       epf->bar[bar].phys_addr = 0;
+       epf->bar[bar].size = 0;
+}
+EXPORT_SYMBOL_GPL(pci_epf_free_space);
+
+/**
+ * pci_epf_alloc_space() - allocate memory for the PCI EPF register space
+ * @size: the size of the memory that has to be allocated
+ * @bar: the BAR number corresponding to the allocated register space
+ *
+ * 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)
+{
+       void *space;
+       struct device *dev = &epf->dev;
+       dma_addr_t phys_addr;
+
+       if (size < 128)
+               size = 128;
+       size = roundup_pow_of_two(size);
+
+       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].size = size;
+
+       return space;
+}
+EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
+
+/**
+ * pci_epf_unregister_driver() - unregister the PCI EPF driver
+ * @driver: the PCI EPF driver that has to be unregistered
+ *
+ * Invoke to unregister the PCI EPF driver.
+ */
+void pci_epf_unregister_driver(struct pci_epf_driver *driver)
+{
+       pci_ep_cfs_remove_epf_group(driver->group);
+       driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(pci_epf_unregister_driver);
+
+/**
+ * __pci_epf_register_driver() - register a new PCI EPF driver
+ * @driver: structure representing PCI EPF driver
+ * @owner: the owner of the module that registers the PCI EPF driver
+ *
+ * Invoke to register a new PCI EPF driver.
+ */
+int __pci_epf_register_driver(struct pci_epf_driver *driver,
+                             struct module *owner)
+{
+       int ret;
+
+       if (!driver->ops)
+               return -EINVAL;
+
+       if (!driver->ops->bind || !driver->ops->unbind || !driver->ops->linkup)
+               return -EINVAL;
+
+       driver->driver.bus = &pci_epf_bus_type;
+       driver->driver.owner = owner;
+
+       ret = driver_register(&driver->driver);
+       if (ret)
+               return ret;
+
+       driver->group = pci_ep_cfs_add_epf_group(driver->driver.name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(__pci_epf_register_driver);
+
+/**
+ * pci_epf_destroy() - destroy the created PCI EPF device
+ * @epf: the PCI EPF device that has to be destroyed.
+ *
+ * Invoke to destroy the PCI EPF device created by invoking pci_epf_create().
+ */
+void pci_epf_destroy(struct pci_epf *epf)
+{
+       device_unregister(&epf->dev);
+}
+EXPORT_SYMBOL_GPL(pci_epf_destroy);
+
+/**
+ * pci_epf_create() - create a new PCI EPF device
+ * @name: the name of the PCI EPF device. This name will be used to bind the
+ *       the EPF device to a EPF driver
+ *
+ * Invoke to create a new PCI EPF device by providing the name of the function
+ * device.
+ */
+struct pci_epf *pci_epf_create(const char *name)
+{
+       int ret;
+       struct pci_epf *epf;
+       struct device *dev;
+       char *func_name;
+       char *buf;
+
+       epf = kzalloc(sizeof(*epf), GFP_KERNEL);
+       if (!epf) {
+               ret = -ENOMEM;
+               goto err_ret;
+       }
+
+       buf = kstrdup(name, GFP_KERNEL);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto free_epf;
+       }
+
+       func_name = buf;
+       buf = strchrnul(buf, '.');
+       *buf = '\0';
+
+       epf->name = kstrdup(func_name, GFP_KERNEL);
+       if (!epf->name) {
+               ret = -ENOMEM;
+               goto free_func_name;
+       }
+
+       dev = &epf->dev;
+       device_initialize(dev);
+       dev->bus = &pci_epf_bus_type;
+       dev->type = &pci_epf_type;
+
+       ret = dev_set_name(dev, "%s", name);
+       if (ret)
+               goto put_dev;
+
+       ret = device_add(dev);
+       if (ret)
+               goto put_dev;
+
+       kfree(func_name);
+       return epf;
+
+put_dev:
+       put_device(dev);
+       kfree(epf->name);
+
+free_func_name:
+       kfree(func_name);
+
+free_epf:
+       kfree(epf);
+
+err_ret:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(pci_epf_create);
+
+static void pci_epf_dev_release(struct device *dev)
+{
+       struct pci_epf *epf = to_pci_epf(dev);
+
+       kfree(epf->name);
+       kfree(epf);
+}
+
+static struct device_type pci_epf_type = {
+       .release        = pci_epf_dev_release,
+};
+
+static int
+pci_epf_match_id(const struct pci_epf_device_id *id, const struct pci_epf *epf)
+{
+       while (id->name[0]) {
+               if (strcmp(epf->name, id->name) == 0)
+                       return true;
+               id++;
+       }
+
+       return false;
+}
+
+static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
+{
+       struct pci_epf *epf = to_pci_epf(dev);
+       struct pci_epf_driver *driver = to_pci_epf_driver(drv);
+
+       if (driver->id_table)
+               return pci_epf_match_id(driver->id_table, epf);
+
+       return !strcmp(epf->name, drv->name);
+}
+
+static int pci_epf_device_probe(struct device *dev)
+{
+       struct pci_epf *epf = to_pci_epf(dev);
+       struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
+
+       if (!driver->probe)
+               return -ENODEV;
+
+       epf->driver = driver;
+
+       return driver->probe(epf);
+}
+
+static int pci_epf_device_remove(struct device *dev)
+{
+       int ret;
+       struct pci_epf *epf = to_pci_epf(dev);
+       struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
+
+       ret = driver->remove(epf);
+       epf->driver = NULL;
+
+       return ret;
+}
+
+static struct bus_type pci_epf_bus_type = {
+       .name           = "pci-epf",
+       .match          = pci_epf_device_match,
+       .probe          = pci_epf_device_probe,
+       .remove         = pci_epf_device_remove,
+};
+
+static int __init pci_epf_init(void)
+{
+       int ret;
+
+       ret = bus_register(&pci_epf_bus_type);
+       if (ret) {
+               pr_err("failed to register pci epf bus --> %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+module_init(pci_epf_init);
+
+static void __exit pci_epf_exit(void)
+{
+       bus_unregister(&pci_epf_bus_type);
+}
+module_exit(pci_epf_exit);
+
+MODULE_DESCRIPTION("PCI EPF Library");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
index d2293ed81cf947487370a1eab7d1e95a82108f9d..7f47cd5e10a57c4f7de8f175b3034fe375d6785f 100644 (file)
@@ -27,6 +27,12 @@ config PCIE_XILINX_NWL
         or End Point. The current option selection will only
         support root port enabling.
 
+config PCI_FTPCI100
+       bool "Faraday Technology FTPCI100 PCI controller"
+       depends on OF
+       depends on ARM
+       default ARCH_GEMINI
+
 config PCI_TEGRA
        bool "NVIDIA Tegra PCIe controller"
        depends on ARCH_TEGRA
@@ -95,6 +101,7 @@ config PCI_VERSATILE
 
 config PCIE_IPROC
        tristate
+       select PCI_DOMAINS
        help
          This enables the iProc PCIe core controller support for Broadcom's
          iProc family of SoCs. An appropriate bus interface driver needs
@@ -115,7 +122,6 @@ config PCIE_IPROC_BCMA
        depends on ARM && (ARCH_BCM_IPROC || COMPILE_TEST)
        select PCIE_IPROC
        select BCMA
-       select PCI_DOMAINS
        default ARCH_BCM_5301X
        help
          Say Y here if you want to use the Broadcom iProc PCIe controller
index 4d3686676cc3c9ea21ae2e9933f4208631fc09d7..cab879578003f6c287adbbb058d00aab5d90ff99 100644 (file)
@@ -1,3 +1,4 @@
+obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o
 obj-$(CONFIG_PCI_HYPERV) += pci-hyperv.o
 obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
 obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c
new file mode 100644 (file)
index 0000000..c0f29d1
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * Support for Faraday Technology FTPC100 PCI Controller
+ *
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Based on the out-of-tree OpenWRT patch for Cortina Gemini:
+ * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
+ * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+ * Based on SL2312 PCI controller code
+ * Storlink (C) 2003
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+
+/*
+ * Special configuration registers directly in the first few words
+ * in I/O space.
+ */
+#define PCI_IOSIZE     0x00
+#define PCI_PROT       0x04 /* AHB protection */
+#define PCI_CTRL       0x08 /* PCI control signal */
+#define PCI_SOFTRST    0x10 /* Soft reset counter and response error enable */
+#define PCI_CONFIG     0x28 /* PCI configuration command register */
+#define PCI_DATA       0x2C
+
+#define FARADAY_PCI_PMC                        0x40 /* Power management control */
+#define FARADAY_PCI_PMCSR              0x44 /* Power management status */
+#define FARADAY_PCI_CTRL1              0x48 /* Control register 1 */
+#define FARADAY_PCI_CTRL2              0x4C /* Control register 2 */
+#define FARADAY_PCI_MEM1_BASE_SIZE     0x50 /* Memory base and size #1 */
+#define FARADAY_PCI_MEM2_BASE_SIZE     0x54 /* Memory base and size #2 */
+#define FARADAY_PCI_MEM3_BASE_SIZE     0x58 /* Memory base and size #3 */
+
+/* Bits 31..28 gives INTD..INTA status */
+#define PCI_CTRL2_INTSTS_SHIFT         28
+#define PCI_CTRL2_INTMASK_CMDERR       BIT(27)
+#define PCI_CTRL2_INTMASK_PARERR       BIT(26)
+/* Bits 25..22 masks INTD..INTA */
+#define PCI_CTRL2_INTMASK_SHIFT                22
+#define PCI_CTRL2_INTMASK_MABRT_RX     BIT(21)
+#define PCI_CTRL2_INTMASK_TABRT_RX     BIT(20)
+#define PCI_CTRL2_INTMASK_TABRT_TX     BIT(19)
+#define PCI_CTRL2_INTMASK_RETRY4       BIT(18)
+#define PCI_CTRL2_INTMASK_SERR_RX      BIT(17)
+#define PCI_CTRL2_INTMASK_PERR_RX      BIT(16)
+/* Bit 15 reserved */
+#define PCI_CTRL2_MSTPRI_REQ6          BIT(14)
+#define PCI_CTRL2_MSTPRI_REQ5          BIT(13)
+#define PCI_CTRL2_MSTPRI_REQ4          BIT(12)
+#define PCI_CTRL2_MSTPRI_REQ3          BIT(11)
+#define PCI_CTRL2_MSTPRI_REQ2          BIT(10)
+#define PCI_CTRL2_MSTPRI_REQ1          BIT(9)
+#define PCI_CTRL2_MSTPRI_REQ0          BIT(8)
+/* Bits 7..4 reserved */
+/* Bits 3..0 TRDYW */
+
+/*
+ * Memory configs:
+ * Bit 31..20 defines the PCI side memory base
+ * Bit 19..16 (4 bits) defines the size per below
+ */
+#define FARADAY_PCI_MEMBASE_MASK       0xfff00000
+#define FARADAY_PCI_MEMSIZE_1MB                0x0
+#define FARADAY_PCI_MEMSIZE_2MB                0x1
+#define FARADAY_PCI_MEMSIZE_4MB                0x2
+#define FARADAY_PCI_MEMSIZE_8MB                0x3
+#define FARADAY_PCI_MEMSIZE_16MB       0x4
+#define FARADAY_PCI_MEMSIZE_32MB       0x5
+#define FARADAY_PCI_MEMSIZE_64MB       0x6
+#define FARADAY_PCI_MEMSIZE_128MB      0x7
+#define FARADAY_PCI_MEMSIZE_256MB      0x8
+#define FARADAY_PCI_MEMSIZE_512MB      0x9
+#define FARADAY_PCI_MEMSIZE_1GB                0xa
+#define FARADAY_PCI_MEMSIZE_2GB                0xb
+#define FARADAY_PCI_MEMSIZE_SHIFT      16
+
+/*
+ * The DMA base is set to 0x0 for all memory segments, it reflects the
+ * fact that the memory of the host system starts at 0x0.
+ */
+#define FARADAY_PCI_DMA_MEM1_BASE      0x00000000
+#define FARADAY_PCI_DMA_MEM2_BASE      0x00000000
+#define FARADAY_PCI_DMA_MEM3_BASE      0x00000000
+
+/* Defines for PCI configuration command register */
+#define PCI_CONF_ENABLE                BIT(31)
+#define PCI_CONF_WHERE(r)      ((r) & 0xFC)
+#define PCI_CONF_BUS(b)                (((b) & 0xFF) << 16)
+#define PCI_CONF_DEVICE(d)     (((d) & 0x1F) << 11)
+#define PCI_CONF_FUNCTION(f)   (((f) & 0x07) << 8)
+
+/**
+ * struct faraday_pci_variant - encodes IP block differences
+ * @cascaded_irq: this host has cascaded IRQs from an interrupt controller
+ *     embedded in the host bridge.
+ */
+struct faraday_pci_variant {
+       bool cascaded_irq;
+};
+
+struct faraday_pci {
+       struct device *dev;
+       void __iomem *base;
+       struct irq_domain *irqdomain;
+       struct pci_bus *bus;
+};
+
+static int faraday_res_to_memcfg(resource_size_t mem_base,
+                                resource_size_t mem_size, u32 *val)
+{
+       u32 outval;
+
+       switch (mem_size) {
+       case SZ_1M:
+               outval = FARADAY_PCI_MEMSIZE_1MB;
+               break;
+       case SZ_2M:
+               outval = FARADAY_PCI_MEMSIZE_2MB;
+               break;
+       case SZ_4M:
+               outval = FARADAY_PCI_MEMSIZE_4MB;
+               break;
+       case SZ_8M:
+               outval = FARADAY_PCI_MEMSIZE_8MB;
+               break;
+       case SZ_16M:
+               outval = FARADAY_PCI_MEMSIZE_16MB;
+               break;
+       case SZ_32M:
+               outval = FARADAY_PCI_MEMSIZE_32MB;
+               break;
+       case SZ_64M:
+               outval = FARADAY_PCI_MEMSIZE_64MB;
+               break;
+       case SZ_128M:
+               outval = FARADAY_PCI_MEMSIZE_128MB;
+               break;
+       case SZ_256M:
+               outval = FARADAY_PCI_MEMSIZE_256MB;
+               break;
+       case SZ_512M:
+               outval = FARADAY_PCI_MEMSIZE_512MB;
+               break;
+       case SZ_1G:
+               outval = FARADAY_PCI_MEMSIZE_1GB;
+               break;
+       case SZ_2G:
+               outval = FARADAY_PCI_MEMSIZE_2GB;
+               break;
+       default:
+               return -EINVAL;
+       }
+       outval <<= FARADAY_PCI_MEMSIZE_SHIFT;
+
+       /* This is probably not good */
+       if (mem_base & ~(FARADAY_PCI_MEMBASE_MASK))
+               pr_warn("truncated PCI memory base\n");
+       /* Translate to bridge side address space */
+       outval |= (mem_base & FARADAY_PCI_MEMBASE_MASK);
+       pr_debug("Translated pci base @%pap, size %pap to config %08x\n",
+                &mem_base, &mem_size, outval);
+
+       *val = outval;
+       return 0;
+}
+
+static int faraday_pci_read_config(struct pci_bus *bus, unsigned int fn,
+                                  int config, int size, u32 *value)
+{
+       struct faraday_pci *p = bus->sysdata;
+
+       writel(PCI_CONF_BUS(bus->number) |
+                       PCI_CONF_DEVICE(PCI_SLOT(fn)) |
+                       PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
+                       PCI_CONF_WHERE(config) |
+                       PCI_CONF_ENABLE,
+                       p->base + PCI_CONFIG);
+
+       *value = readl(p->base + PCI_DATA);
+
+       if (size == 1)
+               *value = (*value >> (8 * (config & 3))) & 0xFF;
+       else if (size == 2)
+               *value = (*value >> (8 * (config & 3))) & 0xFFFF;
+
+       dev_dbg(&bus->dev,
+               "[read]  slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
+               PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value);
+
+       return PCIBIOS_SUCCESSFUL;
+}
+
+static int faraday_pci_write_config(struct pci_bus *bus, unsigned int fn,
+                                   int config, int size, u32 value)
+{
+       struct faraday_pci *p = bus->sysdata;
+       int ret = PCIBIOS_SUCCESSFUL;
+
+       dev_dbg(&bus->dev,
+               "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n",
+               PCI_SLOT(fn), PCI_FUNC(fn), config, size, value);
+
+       writel(PCI_CONF_BUS(bus->number) |
+                       PCI_CONF_DEVICE(PCI_SLOT(fn)) |
+                       PCI_CONF_FUNCTION(PCI_FUNC(fn)) |
+                       PCI_CONF_WHERE(config) |
+                       PCI_CONF_ENABLE,
+                       p->base + PCI_CONFIG);
+
+       switch (size) {
+       case 4:
+               writel(value, p->base + PCI_DATA);
+               break;
+       case 2:
+               writew(value, p->base + PCI_DATA + (config & 3));
+               break;
+       case 1:
+               writeb(value, p->base + PCI_DATA + (config & 3));
+               break;
+       default:
+               ret = PCIBIOS_BAD_REGISTER_NUMBER;
+       }
+
+       return ret;
+}
+
+static struct pci_ops faraday_pci_ops = {
+       .read   = faraday_pci_read_config,
+       .write  = faraday_pci_write_config,
+};
+
+static void faraday_pci_ack_irq(struct irq_data *d)
+{
+       struct faraday_pci *p = irq_data_get_irq_chip_data(d);
+       unsigned int reg;
+
+       faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, &reg);
+       reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT);
+       reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTSTS_SHIFT);
+       faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, reg);
+}
+
+static void faraday_pci_mask_irq(struct irq_data *d)
+{
+       struct faraday_pci *p = irq_data_get_irq_chip_data(d);
+       unsigned int reg;
+
+       faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, &reg);
+       reg &= ~((0xF << PCI_CTRL2_INTSTS_SHIFT)
+                | BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT));
+       faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, reg);
+}
+
+static void faraday_pci_unmask_irq(struct irq_data *d)
+{
+       struct faraday_pci *p = irq_data_get_irq_chip_data(d);
+       unsigned int reg;
+
+       faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, &reg);
+       reg &= ~(0xF << PCI_CTRL2_INTSTS_SHIFT);
+       reg |= BIT(irqd_to_hwirq(d) + PCI_CTRL2_INTMASK_SHIFT);
+       faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, reg);
+}
+
+static void faraday_pci_irq_handler(struct irq_desc *desc)
+{
+       struct faraday_pci *p = irq_desc_get_handler_data(desc);
+       struct irq_chip *irqchip = irq_desc_get_chip(desc);
+       unsigned int irq_stat, reg, i;
+
+       faraday_pci_read_config(p->bus, 0, FARADAY_PCI_CTRL2, 4, &reg);
+       irq_stat = reg >> PCI_CTRL2_INTSTS_SHIFT;
+
+       chained_irq_enter(irqchip, desc);
+
+       for (i = 0; i < 4; i++) {
+               if ((irq_stat & BIT(i)) == 0)
+                       continue;
+               generic_handle_irq(irq_find_mapping(p->irqdomain, i));
+       }
+
+       chained_irq_exit(irqchip, desc);
+}
+
+static struct irq_chip faraday_pci_irq_chip = {
+       .name = "PCI",
+       .irq_ack = faraday_pci_ack_irq,
+       .irq_mask = faraday_pci_mask_irq,
+       .irq_unmask = faraday_pci_unmask_irq,
+};
+
+static int faraday_pci_irq_map(struct irq_domain *domain, unsigned int irq,
+                              irq_hw_number_t hwirq)
+{
+       irq_set_chip_and_handler(irq, &faraday_pci_irq_chip, handle_level_irq);
+       irq_set_chip_data(irq, domain->host_data);
+
+       return 0;
+}
+
+static const struct irq_domain_ops faraday_pci_irqdomain_ops = {
+       .map = faraday_pci_irq_map,
+};
+
+static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p)
+{
+       struct device_node *intc = of_get_next_child(p->dev->of_node, NULL);
+       int irq;
+       int i;
+
+       if (!intc) {
+               dev_err(p->dev, "missing child interrupt-controller node\n");
+               return -EINVAL;
+       }
+
+       /* All PCI IRQs cascade off this one */
+       irq = of_irq_get(intc, 0);
+       if (!irq) {
+               dev_err(p->dev, "failed to get parent IRQ\n");
+               return -EINVAL;
+       }
+
+       p->irqdomain = irq_domain_add_linear(intc, 4,
+                                            &faraday_pci_irqdomain_ops, p);
+       if (!p->irqdomain) {
+               dev_err(p->dev, "failed to create Gemini PCI IRQ domain\n");
+               return -EINVAL;
+       }
+
+       irq_set_chained_handler_and_data(irq, faraday_pci_irq_handler, p);
+
+       for (i = 0; i < 4; i++)
+               irq_create_mapping(p->irqdomain, i);
+
+       return 0;
+}
+
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
+                                    struct device_node *node)
+{
+       const int na = 3, ns = 2;
+       int rlen;
+
+       parser->node = node;
+       parser->pna = of_n_addr_cells(node);
+       parser->np = parser->pna + na + ns;
+
+       parser->range = of_get_property(node, "dma-ranges", &rlen);
+       if (!parser->range)
+               return -ENOENT;
+       parser->end = parser->range + rlen / sizeof(__be32);
+
+       return 0;
+}
+
+static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p,
+                                           struct device_node *np)
+{
+       struct of_pci_range range;
+       struct of_pci_range_parser parser;
+       struct device *dev = p->dev;
+       u32 confreg[3] = {
+               FARADAY_PCI_MEM1_BASE_SIZE,
+               FARADAY_PCI_MEM2_BASE_SIZE,
+               FARADAY_PCI_MEM3_BASE_SIZE,
+       };
+       int i = 0;
+       u32 val;
+
+       if (pci_dma_range_parser_init(&parser, np)) {
+               dev_err(dev, "missing dma-ranges property\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Get the dma-ranges from the device tree
+        */
+       for_each_of_pci_range(&parser, &range) {
+               u64 end = range.pci_addr + range.size - 1;
+               int ret;
+
+               ret = faraday_res_to_memcfg(range.pci_addr, range.size, &val);
+               if (ret) {
+                       dev_err(dev,
+                               "DMA range %d: illegal MEM resource size\n", i);
+                       return -EINVAL;
+               }
+
+               dev_info(dev, "DMA MEM%d BASE: 0x%016llx -> 0x%016llx config %08x\n",
+                        i + 1, range.pci_addr, end, val);
+               if (i <= 2) {
+                       faraday_pci_write_config(p->bus, 0, confreg[i],
+                                                4, val);
+               } else {
+                       dev_err(dev, "ignore extraneous dma-range %d\n", i);
+                       break;
+               }
+
+               i++;
+       }
+
+       return 0;
+}
+
+static int faraday_pci_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct faraday_pci_variant *variant =
+               of_device_get_match_data(dev);
+       struct resource *regs;
+       resource_size_t io_base;
+       struct resource_entry *win;
+       struct faraday_pci *p;
+       struct resource *mem;
+       struct resource *io;
+       struct pci_host_bridge *host;
+       int ret;
+       u32 val;
+       LIST_HEAD(res);
+
+       host = pci_alloc_host_bridge(sizeof(*p));
+       if (!host)
+               return -ENOMEM;
+
+       host->dev.parent = dev;
+       host->ops = &faraday_pci_ops;
+       host->busnr = 0;
+       host->msi = NULL;
+       p = pci_host_bridge_priv(host);
+       host->sysdata = p;
+       p->dev = dev;
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       p->base = devm_ioremap_resource(dev, regs);
+       if (IS_ERR(p->base))
+               return PTR_ERR(p->base);
+
+       ret = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
+                                              &res, &io_base);
+       if (ret)
+               return ret;
+
+       ret = devm_request_pci_bus_resources(dev, &res);
+       if (ret)
+               return ret;
+
+       /* Get the I/O and memory ranges from DT */
+       resource_list_for_each_entry(win, &res) {
+               switch (resource_type(win->res)) {
+               case IORESOURCE_IO:
+                       io = win->res;
+                       io->name = "Gemini PCI I/O";
+                       if (!faraday_res_to_memcfg(io->start - win->offset,
+                                                  resource_size(io), &val)) {
+                               /* setup I/O space size */
+                               writel(val, p->base + PCI_IOSIZE);
+                       } else {
+                               dev_err(dev, "illegal IO mem size\n");
+                               return -EINVAL;
+                       }
+                       ret = pci_remap_iospace(io, io_base);
+                       if (ret) {
+                               dev_warn(dev, "error %d: failed to map resource %pR\n",
+                                        ret, io);
+                               continue;
+                       }
+                       break;
+               case IORESOURCE_MEM:
+                       mem = win->res;
+                       mem->name = "Gemini PCI MEM";
+                       break;
+               case IORESOURCE_BUS:
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /* Setup hostbridge */
+       val = readl(p->base + PCI_CTRL);
+       val |= PCI_COMMAND_IO;
+       val |= PCI_COMMAND_MEMORY;
+       val |= PCI_COMMAND_MASTER;
+       writel(val, p->base + PCI_CTRL);
+
+       list_splice_init(&res, &host->windows);
+       ret = pci_register_host_bridge(host);
+       if (ret) {
+               dev_err(dev, "failed to register host: %d\n", ret);
+               return ret;
+       }
+       p->bus = host->bus;
+
+       /* Mask and clear all interrupts */
+       faraday_pci_write_config(p->bus, 0, FARADAY_PCI_CTRL2 + 2, 2, 0xF000);
+       if (variant->cascaded_irq) {
+               ret = faraday_pci_setup_cascaded_irq(p);
+               if (ret) {
+                       dev_err(dev, "failed to setup cascaded IRQ\n");
+                       return ret;
+               }
+       }
+
+       ret = faraday_pci_parse_map_dma_ranges(p, dev->of_node);
+       if (ret)
+               return ret;
+
+       pci_scan_child_bus(p->bus);
+       pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+       pci_bus_assign_resources(p->bus);
+       pci_bus_add_devices(p->bus);
+       pci_free_resource_list(&res);
+
+       return 0;
+}
+
+/*
+ * We encode bridge variants here, we have at least two so it doesn't
+ * hurt to have infrastructure to encompass future variants as well.
+ */
+const struct faraday_pci_variant faraday_regular = {
+       .cascaded_irq = true,
+};
+
+const struct faraday_pci_variant faraday_dual = {
+       .cascaded_irq = false,
+};
+
+static const struct of_device_id faraday_pci_of_match[] = {
+       {
+               .compatible = "faraday,ftpci100",
+               .data = &faraday_regular,
+       },
+       {
+               .compatible = "faraday,ftpci100-dual",
+               .data = &faraday_dual,
+       },
+       {},
+};
+
+static struct platform_driver faraday_pci_driver = {
+       .driver = {
+               .name = "ftpci100",
+               .of_match_table = of_match_ptr(faraday_pci_of_match),
+       },
+       .probe  = faraday_pci_probe,
+};
+builtin_platform_driver(faraday_pci_driver);
index ada98569b78ed65561d5fb4479f1eeee62cd00c3..84936383e26911ea7c634698b8f53c053d236a1e 100644 (file)
@@ -56,6 +56,7 @@
 #include <asm/apic.h>
 #include <linux/msi.h>
 #include <linux/hyperv.h>
+#include <linux/refcount.h>
 #include <asm/mshyperv.h>
 
 /*
@@ -72,6 +73,7 @@ enum {
        PCI_PROTOCOL_VERSION_CURRENT = PCI_PROTOCOL_VERSION_1_1
 };
 
+#define CPU_AFFINITY_ALL       -1ULL
 #define PCI_CONFIG_MMIO_LENGTH 0x2000
 #define CFG_PAGE_OFFSET 0x1000
 #define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET)
@@ -350,6 +352,7 @@ enum hv_pcibus_state {
        hv_pcibus_init = 0,
        hv_pcibus_probed,
        hv_pcibus_installed,
+       hv_pcibus_removed,
        hv_pcibus_maximum
 };
 
@@ -421,7 +424,7 @@ enum hv_pcidev_ref_reason {
 struct hv_pci_dev {
        /* List protected by pci_rescan_remove_lock */
        struct list_head list_entry;
-       atomic_t refs;
+       refcount_t refs;
        enum hv_pcichild_state state;
        struct pci_function_description desc;
        bool reported_missing;
@@ -876,7 +879,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                hv_int_desc_free(hpdev, int_desc);
        }
 
-       int_desc = kzalloc(sizeof(*int_desc), GFP_KERNEL);
+       int_desc = kzalloc(sizeof(*int_desc), GFP_ATOMIC);
        if (!int_desc)
                goto drop_reference;
 
@@ -897,9 +900,13 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
         * processors because Hyper-V only supports 64 in a guest.
         */
        affinity = irq_data_get_affinity_mask(data);
-       for_each_cpu_and(cpu, affinity, cpu_online_mask) {
-               int_pkt->int_desc.cpu_mask |=
-                       (1ULL << vmbus_cpu_number_to_vp_number(cpu));
+       if (cpumask_weight(affinity) >= 32) {
+               int_pkt->int_desc.cpu_mask = CPU_AFFINITY_ALL;
+       } else {
+               for_each_cpu_and(cpu, affinity, cpu_online_mask) {
+                       int_pkt->int_desc.cpu_mask |=
+                               (1ULL << vmbus_cpu_number_to_vp_number(cpu));
+               }
        }
 
        ret = vmbus_sendpacket(hpdev->hbus->hdev->channel, int_pkt,
@@ -1208,9 +1215,11 @@ static int create_root_hv_pci_bus(struct hv_pcibus_device *hbus)
        hbus->pci_bus->msi = &hbus->msi_chip;
        hbus->pci_bus->msi->dev = &hbus->hdev->device;
 
+       pci_lock_rescan_remove();
        pci_scan_child_bus(hbus->pci_bus);
        pci_bus_assign_resources(hbus->pci_bus);
        pci_bus_add_devices(hbus->pci_bus);
+       pci_unlock_rescan_remove();
        hbus->state = hv_pcibus_installed;
        return 0;
 }
@@ -1254,13 +1263,13 @@ static void q_resource_requirements(void *context, struct pci_response *resp,
 static void get_pcichild(struct hv_pci_dev *hpdev,
                            enum hv_pcidev_ref_reason reason)
 {
-       atomic_inc(&hpdev->refs);
+       refcount_inc(&hpdev->refs);
 }
 
 static void put_pcichild(struct hv_pci_dev *hpdev,
                            enum hv_pcidev_ref_reason reason)
 {
-       if (atomic_dec_and_test(&hpdev->refs))
+       if (refcount_dec_and_test(&hpdev->refs))
                kfree(hpdev);
 }
 
@@ -1314,7 +1323,7 @@ static struct hv_pci_dev *new_pcichild_device(struct hv_pcibus_device *hbus,
        wait_for_completion(&comp_pkt.host_event);
 
        hpdev->desc = *desc;
-       get_pcichild(hpdev, hv_pcidev_ref_initial);
+       refcount_set(&hpdev->refs, 1);
        get_pcichild(hpdev, hv_pcidev_ref_childlist);
        spin_lock_irqsave(&hbus->device_list_lock, flags);
 
@@ -1504,13 +1513,24 @@ static void pci_devices_present_work(struct work_struct *work)
                put_pcichild(hpdev, hv_pcidev_ref_initial);
        }
 
-       /* Tell the core to rescan bus because there may have been changes. */
-       if (hbus->state == hv_pcibus_installed) {
+       switch(hbus->state) {
+       case hv_pcibus_installed:
+               /*
+               * Tell the core to rescan bus
+               * because there may have been changes.
+               */
                pci_lock_rescan_remove();
                pci_scan_child_bus(hbus->pci_bus);
                pci_unlock_rescan_remove();
-       } else {
+               break;
+
+       case hv_pcibus_init:
+       case hv_pcibus_probed:
                survey_child_resources(hbus);
+               break;
+
+       default:
+               break;
        }
 
        up(&hbus->enum_sem);
@@ -1600,8 +1620,10 @@ static void hv_eject_device_work(struct work_struct *work)
        pdev = pci_get_domain_bus_and_slot(hpdev->hbus->sysdata.domain, 0,
                                           wslot);
        if (pdev) {
+               pci_lock_rescan_remove();
                pci_stop_and_remove_bus_device(pdev);
                pci_dev_put(pdev);
+               pci_unlock_rescan_remove();
        }
 
        spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags);
@@ -2185,6 +2207,7 @@ static int hv_pci_probe(struct hv_device *hdev,
        hbus = kzalloc(sizeof(*hbus), GFP_KERNEL);
        if (!hbus)
                return -ENOMEM;
+       hbus->state = hv_pcibus_init;
 
        /*
         * The PCI bus "domain" is what is called "segment" in ACPI and
@@ -2348,6 +2371,7 @@ static int hv_pci_remove(struct hv_device *hdev)
                pci_stop_root_bus(hbus->pci_bus);
                pci_remove_root_bus(hbus->pci_bus);
                pci_unlock_rescan_remove();
+               hbus->state = hv_pcibus_removed;
        }
 
        hv_pci_bus_exit(hdev);
index cd7d51988738e288e8269f0c016ec0d31aa99c13..266447ee886782c503d1f4de51f5982af21d842d 100644 (file)
@@ -752,10 +752,11 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
                 * If the mask is 0xffff0000, then we only want to write
                 * the link control register, rather than clearing the
                 * RW1C bits in the link status register.  Mask out the
-                * status register bits.
+                * RW1C status register bits.
                 */
                if (mask == 0xffff0000)
-                       value &= 0xffff;
+                       value &= ~((PCI_EXP_LNKSTA_LABS |
+                                   PCI_EXP_LNKSTA_LBMS) << 16);
 
                mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
                break;
index 52b5bdccf5f0c2ab462cc695e332a8b3e3234970..6e031b522529daec0240022cf5221e92be3ea29b 100644 (file)
@@ -14,6 +14,7 @@
  * Copyright (C) 2015 - 2016 Cavium, Inc.
  */
 
+#include <linux/bitfield.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/of_address.h>
@@ -334,6 +335,49 @@ static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg,
 
 #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
 
+#define PEM_RES_BASE           0x87e0c0000000UL
+#define PEM_NODE_MASK          GENMASK(45, 44)
+#define PEM_INDX_MASK          GENMASK(26, 24)
+#define PEM_MIN_DOM_IN_NODE    4
+#define PEM_MAX_DOM_IN_NODE    10
+
+static void thunder_pem_reserve_range(struct device *dev, int seg,
+                                     struct resource *r)
+{
+       resource_size_t start = r->start, end = r->end;
+       struct resource *res;
+       const char *regionid;
+
+       regionid = kasprintf(GFP_KERNEL, "PEM RC:%d", seg);
+       if (!regionid)
+               return;
+
+       res = request_mem_region(start, end - start + 1, regionid);
+       if (res)
+               res->flags &= ~IORESOURCE_BUSY;
+       else
+               kfree(regionid);
+
+       dev_info(dev, "%pR %s reserved\n", r,
+                res ? "has been" : "could not be");
+}
+
+static void thunder_pem_legacy_fw(struct acpi_pci_root *root,
+                                struct resource *res_pem)
+{
+       int node = acpi_get_node(root->device->handle);
+       int index;
+
+       if (node == NUMA_NO_NODE)
+               node = 0;
+
+       index = root->segment - PEM_MIN_DOM_IN_NODE;
+       index -= node * PEM_MAX_DOM_IN_NODE;
+       res_pem->start = PEM_RES_BASE | FIELD_PREP(PEM_NODE_MASK, node) |
+                                       FIELD_PREP(PEM_INDX_MASK, index);
+       res_pem->flags = IORESOURCE_MEM;
+}
+
 static int thunder_pem_acpi_init(struct pci_config_window *cfg)
 {
        struct device *dev = cfg->parent;
@@ -346,10 +390,24 @@ static int thunder_pem_acpi_init(struct pci_config_window *cfg)
        if (!res_pem)
                return -ENOMEM;
 
-       ret = acpi_get_rc_resources(dev, "THRX0002", root->segment, res_pem);
+       ret = acpi_get_rc_resources(dev, "CAVA02B", root->segment, res_pem);
+
+       /*
+        * If we fail to gather resources it means that we run with old
+        * FW where we need to calculate PEM-specific resources manually.
+        */
        if (ret) {
-               dev_err(dev, "can't get rc base address\n");
-               return ret;
+               thunder_pem_legacy_fw(root, res_pem);
+               /*
+                * Reserve 64K size PEM specific resources. The full 16M range
+                * size is required for thunder_pem_init() call.
+                */
+               res_pem->end = res_pem->start + SZ_64K - 1;
+               thunder_pem_reserve_range(dev, root->segment, res_pem);
+               res_pem->end = res_pem->start + SZ_16M - 1;
+
+               /* Reserve PCI configuration space as well. */
+               thunder_pem_reserve_range(dev, root->segment, &cfg->res);
        }
 
        return thunder_pem_init(dev, cfg, res_pem);
index bd4c9ec25edc22531ae450b2bb08f1d2aecd7b62..384c27e664fec8aa777246dce0bca499728ae42a 100644 (file)
@@ -44,8 +44,7 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
 {
        struct device *dev = &bdev->dev;
        struct iproc_pcie *pcie;
-       LIST_HEAD(res);
-       struct resource res_mem;
+       LIST_HEAD(resources);
        int ret;
 
        pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
@@ -63,22 +62,23 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
 
        pcie->base_addr = bdev->addr;
 
-       res_mem.start = bdev->addr_s[0];
-       res_mem.end = bdev->addr_s[0] + SZ_128M - 1;
-       res_mem.name = "PCIe MEM space";
-       res_mem.flags = IORESOURCE_MEM;
-       pci_add_resource(&res, &res_mem);
+       pcie->mem.start = bdev->addr_s[0];
+       pcie->mem.end = bdev->addr_s[0] + SZ_128M - 1;
+       pcie->mem.name = "PCIe MEM space";
+       pcie->mem.flags = IORESOURCE_MEM;
+       pci_add_resource(&resources, &pcie->mem);
 
        pcie->map_irq = iproc_pcie_bcma_map_irq;
 
-       ret = iproc_pcie_setup(pcie, &res);
-       if (ret)
+       ret = iproc_pcie_setup(pcie, &resources);
+       if (ret) {
                dev_err(dev, "PCIe controller setup failed\n");
-
-       pci_free_resource_list(&res);
+               pci_free_resource_list(&resources);
+               return ret;
+       }
 
        bcma_set_drvdata(bdev, pcie);
-       return ret;
+       return 0;
 }
 
 static void iproc_pcie_bcma_remove(struct bcma_device *bdev)
index f4909bb0b2ad1505c3e084820f36104387add8f5..8c6a327ca6cdf883f32ea9a6fa0e7e1e28ab0e98 100644 (file)
@@ -51,7 +51,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
        struct device_node *np = dev->of_node;
        struct resource reg;
        resource_size_t iobase = 0;
-       LIST_HEAD(res);
+       LIST_HEAD(resources);
        int ret;
 
        pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
@@ -96,10 +96,10 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                pcie->phy = NULL;
        }
 
-       ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase);
+       ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &resources,
+                                              &iobase);
        if (ret) {
-               dev_err(dev,
-                       "unable to get PCI host bridge resources\n");
+               dev_err(dev, "unable to get PCI host bridge resources\n");
                return ret;
        }
 
@@ -112,14 +112,15 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                pcie->map_irq = of_irq_parse_and_map_pci;
        }
 
-       ret = iproc_pcie_setup(pcie, &res);
-       if (ret)
+       ret = iproc_pcie_setup(pcie, &resources);
+       if (ret) {
                dev_err(dev, "PCIe controller setup failed\n");
-
-       pci_free_resource_list(&res);
+               pci_free_resource_list(&resources);
+               return ret;
+       }
 
        platform_set_drvdata(pdev, pcie);
-       return ret;
+       return 0;
 }
 
 static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
index 04fed8e907f12b602b5fb1f11ff59971ebd0b87f..0bbe2ea44f3e1559dda22adc85ea70a8862684bd 100644 (file)
@@ -90,6 +90,7 @@ struct iproc_pcie {
 #ifdef CONFIG_ARM
        struct pci_sys_data sysdata;
 #endif
+       struct resource mem;
        struct pci_bus *root_bus;
        struct phy *phy;
        int (*map_irq)(const struct pci_dev *, u8, u8);
index 973472c23d89045000cf1119a09867c921f2fdf8..1dfa10cc566bebed005c2fe11a72c85a37036c32 100644 (file)
@@ -478,7 +478,7 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
 
 static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 {
-       struct pci_dev *child, *parent = link->pdev;
+       struct pci_dev *child = link->downstream, *parent = link->pdev;
        struct pci_bus *linkbus = parent->subordinate;
        struct aspm_register_info upreg, dwreg;
 
@@ -491,9 +491,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
 
        /* Get upstream/downstream components' register state */
        pcie_get_aspm_reg(parent, &upreg);
-       child = pci_function_0(linkbus);
        pcie_get_aspm_reg(child, &dwreg);
-       link->downstream = child;
 
        /*
         * If ASPM not supported, don't mess with the clocks and link,
@@ -800,6 +798,7 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
        INIT_LIST_HEAD(&link->children);
        INIT_LIST_HEAD(&link->link);
        link->pdev = pdev;
+       link->downstream = pci_function_0(pdev->subordinate);
 
        /*
         * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
index f754453fe754e985361cb49cee0bddf54d752443..673683660b5c70567d7c49cd091c5c8ecf088655 100644 (file)
@@ -2174,6 +2174,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID,
                quirk_blacklist_vpd);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_QLOGIC, 0x2261, quirk_blacklist_vpd);
 
 /*
  * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the
index 4585d6105d68af19f72f1fd90663b670d45a8f8e..abbd52466573f9994ea25eab5e6d2b12c00f564e 100644 (file)
@@ -44,4 +44,8 @@
 
 #define IMX7D_GPR5_CSI_MUX_CONTROL_MIPI                (0x1 << 4)
 
+#define IMX7D_GPR12_PCIE_PHY_REFCLK_SEL                BIT(5)
+
+#define IMX7D_GPR22_PCIE_PHY_PLL_LOCKED                BIT(31)
+
 #endif /* __LINUX_IMX7_IOMUXC_GPR_H */
index 8850fcaf50dba05d9440b958c5d95cd9330e727a..566fda587fcf7a76af1c6a01e84ce33336606f3f 100644 (file)
@@ -428,6 +428,16 @@ struct i2c_device_id {
        kernel_ulong_t driver_data;     /* Data private to the driver */
 };
 
+/* pci_epf */
+
+#define PCI_EPF_NAME_SIZE      20
+#define PCI_EPF_MODULE_PREFIX  "pci_epf:"
+
+struct pci_epf_device_id {
+       char name[PCI_EPF_NAME_SIZE];
+       kernel_ulong_t driver_data;
+};
+
 /* spi */
 
 #define SPI_NAME_SIZE  32
diff --git a/include/linux/pci-ep-cfs.h b/include/linux/pci-ep-cfs.h
new file mode 100644 (file)
index 0000000..263b89e
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * PCI Endpoint ConfigFS header file
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_PCI_EP_CFS_H
+#define __LINUX_PCI_EP_CFS_H
+
+#include <linux/configfs.h>
+
+#ifdef CONFIG_PCI_ENDPOINT_CONFIGFS
+struct config_group *pci_ep_cfs_add_epc_group(const char *name);
+void pci_ep_cfs_remove_epc_group(struct config_group *group);
+struct config_group *pci_ep_cfs_add_epf_group(const char *name);
+void pci_ep_cfs_remove_epf_group(struct config_group *group);
+#else
+static inline struct config_group *pci_ep_cfs_add_epc_group(const char *name)
+{
+       return 0;
+}
+
+static inline void pci_ep_cfs_remove_epc_group(struct config_group *group)
+{
+}
+
+static inline struct config_group *pci_ep_cfs_add_epf_group(const char *name)
+{
+       return 0;
+}
+
+static inline void pci_ep_cfs_remove_epf_group(struct config_group *group)
+{
+}
+#endif
+#endif /* __LINUX_PCI_EP_CFS_H */
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
new file mode 100644 (file)
index 0000000..af5edbf
--- /dev/null
@@ -0,0 +1,144 @@
+/**
+ * PCI Endpoint *Controller* (EPC) header file
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_PCI_EPC_H
+#define __LINUX_PCI_EPC_H
+
+#include <linux/pci-epf.h>
+
+struct pci_epc;
+
+enum pci_epc_irq_type {
+       PCI_EPC_IRQ_UNKNOWN,
+       PCI_EPC_IRQ_LEGACY,
+       PCI_EPC_IRQ_MSI,
+};
+
+/**
+ * struct pci_epc_ops - set of function pointers for performing EPC operations
+ * @write_header: ops to populate configuration space header
+ * @set_bar: ops to configure the BAR
+ * @clear_bar: ops to reset the BAR
+ * @map_addr: ops to map CPU address to PCI address
+ * @unmap_addr: ops to unmap CPU address and PCI address
+ * @set_msi: ops to set the requested number of MSI interrupts in the MSI
+ *          capability register
+ * @get_msi: ops to get the number of MSI interrupts allocated by the RC from
+ *          the MSI capability register
+ * @raise_irq: ops to raise a legacy or MSI interrupt
+ * @start: ops to start the PCI link
+ * @stop: ops to stop the PCI link
+ * @owner: the module owner containing the ops
+ */
+struct pci_epc_ops {
+       int     (*write_header)(struct pci_epc *pci_epc,
+                               struct pci_epf_header *hdr);
+       int     (*set_bar)(struct pci_epc *epc, enum pci_barno bar,
+                          dma_addr_t bar_phys, size_t size, int flags);
+       void    (*clear_bar)(struct pci_epc *epc, enum pci_barno bar);
+       int     (*map_addr)(struct pci_epc *epc, phys_addr_t addr,
+                           u64 pci_addr, size_t size);
+       void    (*unmap_addr)(struct pci_epc *epc, phys_addr_t addr);
+       int     (*set_msi)(struct pci_epc *epc, u8 interrupts);
+       int     (*get_msi)(struct pci_epc *epc);
+       int     (*raise_irq)(struct pci_epc *pci_epc,
+                            enum pci_epc_irq_type type, u8 interrupt_num);
+       int     (*start)(struct pci_epc *epc);
+       void    (*stop)(struct pci_epc *epc);
+       struct module *owner;
+};
+
+/**
+ * struct pci_epc_mem - address space of the endpoint controller
+ * @phys_base: physical base address of the PCI address space
+ * @size: the size of the PCI address space
+ * @bitmap: bitmap to manage the PCI address space
+ * @pages: number of bits representing the address region
+ */
+struct pci_epc_mem {
+       phys_addr_t     phys_base;
+       size_t          size;
+       unsigned long   *bitmap;
+       int             pages;
+};
+
+/**
+ * struct pci_epc - represents the PCI EPC device
+ * @dev: PCI EPC device
+ * @pci_epf: list of endpoint functions present in this EPC device
+ * @ops: function pointers for performing endpoint operations
+ * @mem: address space of the endpoint controller
+ * @max_functions: max number of functions that can be configured in this EPC
+ * @group: configfs group representing the PCI EPC device
+ * @lock: spinlock to protect pci_epc ops
+ */
+struct pci_epc {
+       struct device                   dev;
+       struct list_head                pci_epf;
+       const struct pci_epc_ops        *ops;
+       struct pci_epc_mem              *mem;
+       u8                              max_functions;
+       struct config_group             *group;
+       /* spinlock to protect against concurrent access of EP controller */
+       spinlock_t                      lock;
+};
+
+#define to_pci_epc(device) container_of((device), struct pci_epc, dev)
+
+#define pci_epc_create(dev, ops)    \
+               __pci_epc_create((dev), (ops), THIS_MODULE)
+#define devm_pci_epc_create(dev, ops)    \
+               __devm_pci_epc_create((dev), (ops), THIS_MODULE)
+
+static inline void epc_set_drvdata(struct pci_epc *epc, void *data)
+{
+       dev_set_drvdata(&epc->dev, data);
+}
+
+static inline void *epc_get_drvdata(struct pci_epc *epc)
+{
+       return dev_get_drvdata(&epc->dev);
+}
+
+struct pci_epc *
+__devm_pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
+                     struct module *owner);
+struct pci_epc *
+__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);
+void pci_epc_linkup(struct pci_epc *epc);
+void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf);
+int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *hdr);
+int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar,
+                   dma_addr_t bar_phys, size_t size, int flags);
+void pci_epc_clear_bar(struct pci_epc *epc, int bar);
+int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr,
+                    u64 pci_addr, size_t size);
+void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr);
+int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts);
+int pci_epc_get_msi(struct pci_epc *epc);
+int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type,
+                     u8 interrupt_num);
+int pci_epc_start(struct pci_epc *epc);
+void pci_epc_stop(struct pci_epc *epc);
+struct pci_epc *pci_epc_get(const char *epc_name);
+void pci_epc_put(struct pci_epc *epc);
+
+int pci_epc_mem_init(struct pci_epc *epc, phys_addr_t phys_addr, size_t size);
+void pci_epc_mem_exit(struct pci_epc *epc);
+void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
+                                    phys_addr_t *phys_addr, size_t size);
+void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
+                          void __iomem *virt_addr, size_t size);
+#endif /* __LINUX_PCI_EPC_H */
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
new file mode 100644 (file)
index 0000000..0d529cb
--- /dev/null
@@ -0,0 +1,162 @@
+/**
+ * PCI Endpoint *Function* (EPF) header file
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_PCI_EPF_H
+#define __LINUX_PCI_EPF_H
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+struct pci_epf;
+
+enum pci_interrupt_pin {
+       PCI_INTERRUPT_UNKNOWN,
+       PCI_INTERRUPT_INTA,
+       PCI_INTERRUPT_INTB,
+       PCI_INTERRUPT_INTC,
+       PCI_INTERRUPT_INTD,
+};
+
+enum pci_barno {
+       BAR_0,
+       BAR_1,
+       BAR_2,
+       BAR_3,
+       BAR_4,
+       BAR_5,
+};
+
+/**
+ * struct pci_epf_header - represents standard configuration header
+ * @vendorid: identifies device manufacturer
+ * @deviceid: identifies a particular device
+ * @revid: specifies a device-specific revision identifier
+ * @progif_code: identifies a specific register-level programming interface
+ * @subclass_code: identifies more specifically the function of the device
+ * @baseclass_code: broadly classifies the type of function the device performs
+ * @cache_line_size: specifies the system cacheline size in units of DWORDs
+ * @subsys_vendor_id: vendor of the add-in card or subsystem
+ * @subsys_id: id specific to vendor
+ * @interrupt_pin: interrupt pin the device (or device function) uses
+ */
+struct pci_epf_header {
+       u16     vendorid;
+       u16     deviceid;
+       u8      revid;
+       u8      progif_code;
+       u8      subclass_code;
+       u8      baseclass_code;
+       u8      cache_line_size;
+       u16     subsys_vendor_id;
+       u16     subsys_id;
+       enum pci_interrupt_pin interrupt_pin;
+};
+
+/**
+ * struct pci_epf_ops - set of function pointers for performing EPF operations
+ * @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
+ * @linkup: ops to perform when the EPC device has established a connection with
+ *         a host system
+ */
+struct pci_epf_ops {
+       int     (*bind)(struct pci_epf *epf);
+       void    (*unbind)(struct pci_epf *epf);
+       void    (*linkup)(struct pci_epf *epf);
+};
+
+/**
+ * struct pci_epf_driver - represents the PCI EPF driver
+ * @probe: ops to perform when a new EPF device has been bound to the EPF driver
+ * @remove: ops to perform when the binding between the EPF device and EPF
+ *         driver is broken
+ * @driver: PCI EPF driver
+ * @ops: set of function pointers for performing EPF operations
+ * @owner: the owner of the module that registers the PCI EPF driver
+ * @group: configfs group corresponding to the PCI EPF driver
+ * @id_table: identifies EPF devices for probing
+ */
+struct pci_epf_driver {
+       int     (*probe)(struct pci_epf *epf);
+       int     (*remove)(struct pci_epf *epf);
+
+       struct device_driver    driver;
+       struct pci_epf_ops      *ops;
+       struct module           *owner;
+       struct config_group     *group;
+       const struct pci_epf_device_id  *id_table;
+};
+
+#define to_pci_epf_driver(drv) (container_of((drv), struct pci_epf_driver, \
+                               driver))
+
+/**
+ * struct pci_epf_bar - represents the BAR of EPF device
+ * @phys_addr: physical address that should be mapped to the BAR
+ * @size: the size of the address space present in BAR
+ */
+struct pci_epf_bar {
+       dma_addr_t      phys_addr;
+       size_t          size;
+};
+
+/**
+ * struct pci_epf - represents the PCI EPF device
+ * @dev: the PCI EPF device
+ * @name: the name of the PCI EPF device
+ * @header: represents standard configuration header
+ * @bar: represents the BAR of EPF device
+ * @msi_interrupts: number of MSI interrupts required by this function
+ * @func_no: unique function number within this endpoint device
+ * @epc: the EPC device to which this EPF device is bound
+ * @driver: the EPF driver to which this EPF device is bound
+ * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc
+ */
+struct pci_epf {
+       struct device           dev;
+       const char              *name;
+       struct pci_epf_header   *header;
+       struct pci_epf_bar      bar[6];
+       u8                      msi_interrupts;
+       u8                      func_no;
+
+       struct pci_epc          *epc;
+       struct pci_epf_driver   *driver;
+       struct list_head        list;
+};
+
+#define to_pci_epf(epf_dev) container_of((epf_dev), struct pci_epf, dev)
+
+#define pci_epf_register_driver(driver)    \
+               __pci_epf_register_driver((driver), THIS_MODULE)
+
+static inline void epf_set_drvdata(struct pci_epf *epf, void *data)
+{
+       dev_set_drvdata(&epf->dev, data);
+}
+
+static inline void *epf_get_drvdata(struct pci_epf *epf)
+{
+       return dev_get_drvdata(&epf->dev);
+}
+
+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);
+void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar);
+int pci_epf_bind(struct pci_epf *epf);
+void pci_epf_unbind(struct pci_epf *epf);
+void pci_epf_linkup(struct pci_epf *epf);
+#endif /* __LINUX_PCI_EPF_H */
index a4f77feecbb00f84fac0295a2b2745928e9e0472..5f6b71d15393a4d6df153589cee33e42d42ebb26 100644 (file)
 #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_DRA74x                0xb500
+#define PCI_DEVICE_ID_TI_DRA72x                0xb501
 
 #define PCI_VENDOR_ID_SONY             0x104d
 
index dd9820b1c7796b87986443124ad18907b8b719c3..baee6db082870f59132927640eee6f07082eddb7 100644 (file)
@@ -333,6 +333,7 @@ header-y += parport.h
 header-y += patchkey.h
 header-y += pci.h
 header-y += pci_regs.h
+header-y += pcitest.h
 header-y += perf_event.h
 header-y += personality.h
 header-y += pfkeyv2.h
diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h
new file mode 100644 (file)
index 0000000..a6aa10c
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * pcitest.h - PCI test uapi defines
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ */
+
+#ifndef __UAPI_LINUX_PCITEST_H
+#define __UAPI_LINUX_PCITEST_H
+
+#define PCITEST_BAR            _IO('P', 0x1)
+#define PCITEST_LEGACY_IRQ     _IO('P', 0x2)
+#define PCITEST_MSI            _IOW('P', 0x3, int)
+#define PCITEST_WRITE          _IOW('P', 0x4, unsigned long)
+#define PCITEST_READ           _IOW('P', 0x5, unsigned long)
+#define PCITEST_COPY           _IOW('P', 0x6, unsigned long)
+
+#endif /* __UAPI_LINUX_PCITEST_H */
diff --git a/tools/pci/pcitest.c b/tools/pci/pcitest.c
new file mode 100644 (file)
index 0000000..ad54a58
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * Userspace PCI Endpoint Test Module
+ *
+ * Copyright (C) 2017 Texas Instruments
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/pcitest.h>
+
+#define BILLION 1E9
+
+static char *result[] = { "NOT OKAY", "OKAY" };
+
+struct pci_test {
+       char            *device;
+       char            barnum;
+       bool            legacyirq;
+       unsigned int    msinum;
+       bool            read;
+       bool            write;
+       bool            copy;
+       unsigned long   size;
+};
+
+static int run_test(struct pci_test *test)
+{
+       long ret;
+       int fd;
+       struct timespec start, end;
+       double time;
+
+       fd = open(test->device, O_RDWR);
+       if (fd < 0) {
+               perror("can't open PCI Endpoint Test device");
+               return fd;
+       }
+
+       if (test->barnum >= 0 && test->barnum <= 5) {
+               ret = ioctl(fd, PCITEST_BAR, test->barnum);
+               fprintf(stdout, "BAR%d:\t\t", test->barnum);
+               if (ret < 0)
+                       fprintf(stdout, "TEST FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
+       if (test->legacyirq) {
+               ret = ioctl(fd, PCITEST_LEGACY_IRQ, 0);
+               fprintf(stdout, "LEGACY IRQ:\t");
+               if (ret < 0)
+                       fprintf(stdout, "TEST FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
+       if (test->msinum > 0 && test->msinum <= 32) {
+               ret = ioctl(fd, PCITEST_MSI, test->msinum);
+               fprintf(stdout, "MSI%d:\t\t", test->msinum);
+               if (ret < 0)
+                       fprintf(stdout, "TEST FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
+       if (test->write) {
+               ret = ioctl(fd, PCITEST_WRITE, test->size);
+               fprintf(stdout, "WRITE (%7ld bytes):\t\t", test->size);
+               if (ret < 0)
+                       fprintf(stdout, "TEST FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
+       if (test->read) {
+               ret = ioctl(fd, PCITEST_READ, test->size);
+               fprintf(stdout, "READ (%7ld bytes):\t\t", test->size);
+               if (ret < 0)
+                       fprintf(stdout, "TEST FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
+       if (test->copy) {
+               ret = ioctl(fd, PCITEST_COPY, test->size);
+               fprintf(stdout, "COPY (%7ld bytes):\t\t", test->size);
+               if (ret < 0)
+                       fprintf(stdout, "TEST FAILED\n");
+               else
+                       fprintf(stdout, "%s\n", result[ret]);
+       }
+
+       fflush(stdout);
+}
+
+int main(int argc, char **argv)
+{
+       int c;
+       struct pci_test *test;
+
+       test = calloc(1, sizeof(*test));
+       if (!test) {
+               perror("Fail to allocate memory for pci_test\n");
+               return -ENOMEM;
+       }
+
+       /* since '0' is a valid BAR number, initialize it to -1 */
+       test->barnum = -1;
+
+       /* set default size as 100KB */
+       test->size = 0x19000;
+
+       /* set default endpoint device */
+       test->device = "/dev/pci-endpoint-test.0";
+
+       while ((c = getopt(argc, argv, "D:b:m:lrwcs:")) != EOF)
+       switch (c) {
+       case 'D':
+               test->device = optarg;
+               continue;
+       case 'b':
+               test->barnum = atoi(optarg);
+               if (test->barnum < 0 || test->barnum > 5)
+                       goto usage;
+               continue;
+       case 'l':
+               test->legacyirq = true;
+               continue;
+       case 'm':
+               test->msinum = atoi(optarg);
+               if (test->msinum < 1 || test->msinum > 32)
+                       goto usage;
+               continue;
+       case 'r':
+               test->read = true;
+               continue;
+       case 'w':
+               test->write = true;
+               continue;
+       case 'c':
+               test->copy = true;
+               continue;
+       case 's':
+               test->size = strtoul(optarg, NULL, 0);
+               continue;
+       case '?':
+       case 'h':
+       default:
+usage:
+               fprintf(stderr,
+                       "usage: %s [options]\n"
+                       "Options:\n"
+                       "\t-D <dev>             PCI endpoint test device {default: /dev/pci-endpoint-test.0}\n"
+                       "\t-b <bar num>         BAR test (bar number between 0..5)\n"
+                       "\t-m <msi num>         MSI test (msi number between 1..32)\n"
+                       "\t-r                   Read buffer test\n"
+                       "\t-w                   Write buffer test\n"
+                       "\t-c                   Copy buffer test\n"
+                       "\t-s <size>            Size of buffer {default: 100KB}\n",
+                       argv[0]);
+               return -EINVAL;
+       }
+
+       run_test(test);
+       return 0;
+}
diff --git a/tools/pci/pcitest.sh b/tools/pci/pcitest.sh
new file mode 100644 (file)
index 0000000..5442bbe
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+echo "BAR tests"
+echo
+
+bar=0
+
+while [ $bar -lt 6 ]
+do
+       pcitest -b $bar
+       bar=`expr $bar + 1`
+done
+echo
+
+echo "Interrupt tests"
+echo
+
+pcitest -l
+msi=1
+
+while [ $msi -lt 33 ]
+do
+        pcitest -m $msi
+        msi=`expr $msi + 1`
+done
+echo
+
+echo "Read Tests"
+echo
+
+pcitest -r -s 1
+pcitest -r -s 1024
+pcitest -r -s 1025
+pcitest -r -s 1024000
+pcitest -r -s 1024001
+echo
+
+echo "Write Tests"
+echo
+
+pcitest -w -s 1
+pcitest -w -s 1024
+pcitest -w -s 1025
+pcitest -w -s 1024000
+pcitest -w -s 1024001
+echo
+
+echo "Copy Tests"
+echo
+
+pcitest -c -s 1
+pcitest -c -s 1024
+pcitest -c -s 1025
+pcitest -c -s 1024000
+pcitest -c -s 1024001
+echo