Hi All,
Here is v3 patch-series adding drivers for Intel FPGA devices.
The Intel FPGA driver provides interfaces for userspace applications to
configure, enumerate, open, and access FPGA accelerators on platforms
equipped with Intel(R) PCIe based FPGA solutions and enables system
level management functions such as FPGA partial reconfiguration, power
management and virtualization. It also provides a common framework to
support FPGA devices which implement Device Feature List (DFL) and feature
devices under DFL for better code reuse.
This patch series only adds the basic functions for FPGA accelerators and
partial reconfiguration. Patches for more functions, e.g power management
and virtualization, will be submitted after this series gets reviewed.
Patch 1: add a document for Intel FPGA driver overview, including the HW
architecture, driver organization, device enumeration, virtualization and
opens.
Patch 2: add region_id for fpga_image_info data structure, which allows
driver to pass region id information to fpga-mgr for FPGA reconfiguration
function. (Used by Patch 13)
Patch 3: add a 'status' sysfs interface to fpga-mgr class, it reflects
the status of the fpga-mgr including reconfiguration errors. (Used by
Patch 14)
Patch 4-7: add FPGA device feature list support, it provides common
enumeration interfaces which creates container device (FPGA base region)
and all feature devices by walking through all the 'Device Feature Lists'
provided low level drivers.
Patch 8-9: implement Intel FPGA PCIe device driver. It locates all 'Device
Feature Lists' in PCIe device memory and invokes common interfaces from
above device feature list framework to finish the enumeration.
Patch 10-13: implement FPGA Management Engine (FME) driver. It's a
platform driver matching with the FME platform device created by above
device feature list framework during enumeration. Sysfs and device file
ioctls are exposed as user interfaces to allow partial reconfiguration
to Accelerated Function Units (AFUs) from user space applications.
Patch 14-16: implement FPGA manager/bridge/region platform drivers for
Intel FPGA Management Engine (FME). These platform drivers match with
platform devices created by above FME driver, they use the generic
fpga-mgr/bridge/region class infrastructure to implement FPGA partial
reconfiguration function.
Patch 17-21: implement FPGA Accelerated Function Unit (AFU) driver.
It's a platform driver matching with AFU platform device created by above
device feature list framework during enumeration. It provides user
interfaces to expose the AFU MMIO region, map/unmap dma buffer, and
control the port which AFU connects to.
Changes from v2:
- Split common enumeration code from pcie driver to a separated module
which for device feature list support.
- Drop fpga-dev class and switch to use fpga base region as container.
- Update the intel-fpga.txt documentation for new driver organization.
- Rename feature device drivers for future code reuse.
- Rebase code due to fpga APIs changes
- replace bitfields with marco and shift.
- fix typos, checkpatch issue and other comments.
Changes from v1:
- Use GPLv2 license instead of Dual BSD/GPL.
- Move the code to drivers/fpga folder.
- Update the intel-fpga.txt documentation for new driver organization.
- Add documentation for new sysfs interfaces.
- Switch to use common fpga-region interface for partial reconfiguration
(PR) function in FME. It creates fpga-region/fpga-mgr/fpga-bridge
platform devices and leave the implementation to their platform drivers.
- Add platform drivers for FME fpga-mgr/bridge/region platform devices.
- Fix kbuild warnings, typos and other comments.
This patch series depends on the below patchset from Alan Tull.
[PATCH v2 0/5] fpga: don't use drvdata in common fpga code[1]
[1] https://marc.info/?l=linux-fpga&m=151077942606263&w=2
Kang Luwei (3):
fpga: dfl: add FPGA Management Engine driver basic framework
fpga: dfl: fme: add header sub feature support
fpga: dfl: fme: add partial reconfiguration sub feature support
Wu Hao (15):
docs: fpga: add a document for Intel FPGA driver overview
fpga: mgr: add region_id to fpga_image_info
fpga: mgr: add status for fpga-manager
fpga: add device feature list support
fpga: dfl: add chardev support for feature devices
fpga: dfl: adds fpga_cdev_find_port
fpga: intel-dfl-pci: add enumeration for feature devices
fpga: dfl: fme: add FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls
support
fpga: dfl: add fpga manager platform driver for FME
fpga: dfl: add fpga bridge platform driver for FME
fpga: dfl: add fpga region platform driver for FME
fpga: dfl: add FPGA Accelerated Function Unit driver basic framework
fpga: dfl: afu: add header sub feature support
fpga: dfl: afu: add FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls
support
fpga: dfl: afu: add FPGA_PORT_DMA_MAP/UNMAP ioctls support
Xiao Guangrong (2):
fpga: dfl: add feature device infrastructure
fpga: dfl: afu: add user afu sub feature support
Zhang Yi (1):
fpga: add Intel FPGA DFL PCIe device
Documentation/ABI/testing/sysfs-class-fpga-manager | 10 +
.../ABI/testing/sysfs-platform-fpga-dfl-afu | 16 +
.../ABI/testing/sysfs-platform-fpga-dfl-fme | 21 +
.../ABI/testing/sysfs-platform-fpga-dfl-fme-mgr | 8 +
Documentation/fpga/intel-fpga.txt | 261 +++++
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/fpga/Kconfig | 71 ++
drivers/fpga/Makefile | 14 +
drivers/fpga/dfl-afu-dma-region.c | 465 +++++++++
drivers/fpga/dfl-afu-main.c | 478 +++++++++
drivers/fpga/dfl-afu-region.c | 127 +++
drivers/fpga/dfl-afu.h | 72 ++
drivers/fpga/dfl-fme-main.c | 275 +++++
drivers/fpga/dfl-fme-pr.c | 492 +++++++++
drivers/fpga/dfl-fme.h | 57 ++
drivers/fpga/fpga-dfl-fme-br.c | 87 ++
drivers/fpga/fpga-dfl-fme-mgr.c | 318 ++++++
drivers/fpga/fpga-dfl-fme-region.c | 92 ++
drivers/fpga/fpga-dfl.c | 1068 ++++++++++++++++++++
drivers/fpga/fpga-dfl.h | 511 ++++++++++
drivers/fpga/fpga-mgr.c | 28 +
drivers/fpga/intel-dfl-pci.c | 324 ++++++
include/linux/fpga/fpga-mgr.h | 19 +
include/uapi/linux/fpga-dfl.h | 178 ++++
24 files changed, 4993 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-platform-fpga-dfl-afu
create mode 100644 Documentation/ABI/testing/sysfs-platform-fpga-dfl-fme
create mode 100644 Documentation/ABI/testing/sysfs-platform-fpga-dfl-fme-mgr
create mode 100644 Documentation/fpga/intel-fpga.txt
create mode 100644 drivers/fpga/dfl-afu-dma-region.c
create mode 100644 drivers/fpga/dfl-afu-main.c
create mode 100644 drivers/fpga/dfl-afu-region.c
create mode 100644 drivers/fpga/dfl-afu.h
create mode 100644 drivers/fpga/dfl-fme-main.c
create mode 100644 drivers/fpga/dfl-fme-pr.c
create mode 100644 drivers/fpga/dfl-fme.h
create mode 100644 drivers/fpga/fpga-dfl-fme-br.c
create mode 100644 drivers/fpga/fpga-dfl-fme-mgr.c
create mode 100644 drivers/fpga/fpga-dfl-fme-region.c
create mode 100644 drivers/fpga/fpga-dfl.c
create mode 100644 drivers/fpga/fpga-dfl.h
create mode 100644 drivers/fpga/intel-dfl-pci.c
create mode 100644 include/uapi/linux/fpga-dfl.h
--
1.8.3.1
Add a document for Intel FPGA driver overview.
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Signed-off-by: Wu Hao <redacted>
----
v2: added FME fpga-mgr/bridge/region platform driver to driver organization.
updated open discussion per current implementation.
fixed some typos.
v3: use FPGA base region as container device instead of fpga-dev class.
split common enumeration code from pcie driver to functions exposed by
device feature list framework.
update FME performance reporting which supports both integrated (iperf/)
and discrete (dperf/) FPGA solutions.
---
Documentation/fpga/intel-fpga.txt | 261 ++++++++++++++++++++++++++++++++++++++
1 file changed, 261 insertions(+)
create mode 100644 Documentation/fpga/intel-fpga.txt
@@ -0,0 +1,261 @@+===============================================================================+ Intel FPGA driver Overview+-------------------------------------------------------------------------------+ Enno Luebbers <enno.luebbers-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>+ Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>+ Wu Hao <hao.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>++The Intel FPGA driver provides interfaces for userspace applications to+configure, enumerate, open, and access FPGA accelerators on platforms equipped+with Intel(R) FPGA PCIe based solutions and enables system level management+functions such as FPGA reconfiguration, power management, and virtualization.++HW Architecture+===============+From the OS's point of view, the FPGA hardware appears as a regular PCIe device.+The FPGA device memory is organized using a predefined data structure (Device+Feature List). Features supported by the particular FPGA device are exposed+through these data structures, as illustrated below:++ +-------------------------------+ +-------------++ | PF | | VF |+ +-------------------------------+ +-------------++ ^ ^ ^ ^+ | | | |++-----|------------|---------|--------------|-------++| | | | | |+| +-----+ +-------+ +-------+ +-------+ |+| | FME | | Port0 | | Port1 | | Port2 | |+| +-----+ +-------+ +-------+ +-------+ |+| ^ ^ ^ |+| | | | |+| +-------+ +------+ +-------+ |+| | AFU | | AFU | | AFU | |+| +-------+ +------+ +-------+ |+| |+| FPGA PCIe Device |++---------------------------------------------------+++The driver supports PCIe SR-IOV to create virtual functions (VFs) which can be+used to assign individual accelerators to virtual machines.++FME (FPGA Management Engine)+============================+The FPGA Management Engine performs power and thermal management, error+reporting, reconfiguration, performance reporting for integrated and discrete+solution, and other infrastructure functions. Each FPGA has one FME, which is+always accessed through the physical function (PF).++User-space applications can acquire exclusive access to the FME using open(),+and release it using close().++The following functions are exposed through ioctls:++ Get driver API version (FPGA_GET_API_VERSION)+ Check for extensions (FPGA_CHECK_EXTENSION)+ Assign port to PF (FPGA_FME_PORT_ASSIGN)+ Release port from PF (FPGA_FME_PORT_RELEASE)+ Program bitstream (FPGA_FME_PORT_PR)++More functions are exposed through sysfs+(/sys/class/fpga_region/regionX/fpga-dfl-fme.n/):++ Read bitstream ID (bitstream_id)+ Read bitstream metadata (bitstream_metadata)+ Read number of ports (ports_num)+ Read socket ID (socket_id)+ Read performance counters (iperf/ and dperf/)+ Power management (power_mgmt/)+ Thermal management (thermal_mgmt/)+ Error reporting (errors/)++PORT+====+A port represents the interface between the static FPGA fabric (the "blue+bitstream") and a partially reconfigurable region containing an AFU (the "green+bitstream"). It controls the communication from SW to the accelerator and+exposes features such as reset and debug.++A PCIe device may have several ports and each port can be released from PF by+FPGA_FME_PORT_RELEASE ioctl on FME, and exposed through a VF via PCIe sriov+sysfs interface.++AFU+===+An AFU is attached to a port and exposes a 256k MMIO region to be used for+accelerator-specific control registers.++User-space applications can acquire exclusive access to an AFU attached to a+port by using open() on the port device node, and release it using close().++The following functions are exposed through ioctls:++ Get driver API version (FPGA_GET_API_VERSION)+ Check for extensions (FPGA_CHECK_EXTENSION)+ Get port info (FPGA_PORT_GET_INFO)+ Get MMIO region info (FPGA_PORT_GET_REGION_INFO)+ Map DMA buffer (FPGA_PORT_DMA_MAP)+ Unmap DMA buffer (FPGA_PORT_DMA_UNMAP)+ Reset AFU (FPGA_PORT_RESET)+ Enable UMsg (FPGA_PORT_UMSG_ENABLE)+ Disable UMsg (FPGA_PORT_UMSG_DISABLE)+ Set UMsg mode (FPGA_PORT_UMSG_SET_MODE)+ Set UMsg base address (FPGA_PORT_UMSG_SET_BASE_ADDR)++User-space applications can also mmap() accelerator MMIO regions.++More functions are exposed through sysfs:+(/sys/class/fpga_region/<regionX>/<fpga-dfl-port.m>/):++ Read Accelerator GUID (afu_id)+ Error reporting (errors/)++Partial Reconfiguration+=======================+As mentioned above, accelerators can be reconfigured through partial+reconfiguration of a green bitstream file (GBS). The green bitstream must have+been generated for the exact blue bitstream and targeted reconfigurable region+(port) of the FPGA; otherwise, the reconfiguration operation will fail and+possibly cause system instability. This compatibility can be checked by+comparing the interface ID noted in the GBS header against the interface ID+exposed by the FME through sysfs (see above). This check is usually done by+user-space before calling the reconfiguration IOCTL.++FPGA virtualization+===================+To enable accessing an accelerator from applications running in a VM, the+respective AFU's port needs to be assigned to a VF using the following steps:++ a) The PF owns all AFU ports by default. Any port that needs to be reassigned+ to a VF must first be released through the FPGA_FME_PORT_RELEASE ioctl on the+ FME device.++ b) Once N ports are released from PF, then user can use command below to+ enable SRIOV and VFs. Each VF owns only one Port with AFU.++ echo N > $PCI_DEVICE_PATH/sriov_numvfs++ c) Pass through the VFs to VMs++ d) The AFU under VF is accessible from applications in VM (using the same+ driver inside the VF).++Note that an FME can't be assigned to a VF, thus PR and other management+functions are only available via the PF.+++Driver organization+===================++ +-------++------++------+ |+ | FME || FME || FME | |+ | FPGA || FPGA || FPGA | |+ |Manager||Bridge||Region| |+ +-------++------++------+ |+ +-----------------------+ +--------+ | +--------++ | FME | | AFU | | | AFU |+ | Module | | Module | | | Module |+ +-----------------------+ +--------+ | +--------++ +-----------------------+ | +-----------------------++ | FPGA Container Device | | | FPGA Container Device |+ | (FPGA Base Region) | | | (FPGA Base Region) |+ +-----------------------+ | +-----------------------++ +------------------+ | +------------------++ | FPGA PCIE Module | | Virtual | FPGA PCIE Module |+ +------------------+ Host | Machine +------------------++ -------------------------------------- | ------------------------------+ +---------------+ | +---------------++ | PCI PF Device | | | PCI VF Device |+ +---------------+ | +---------------+++The FPGA devices appear as regular PCIe devices; thus, the FPGA PCIe device+driver is always loaded first once a FPGA PCIE PF or VF device is detected. This+driver plays an infrastructural role in the driver architecture. It:++ a) locates the Device Feature Lists in PCIE device BAR memory, handles+ them and related resources to common interfaces from DFL framework+ for enumeration.+ b) supports SRIOV.++The feature device infrastructure provides common interfaces to create container+device (FPGA base region), discover feature devices and their sub features from+the given Device Feature Lists, and create platform devices for feature devices+with related resources under the container device. It also abstracts operations+for sub features and exposes common interfaces to feature device drivers.++The FPGA Management Engine (FME) driver is a platform driver which is loaded+automatically after FME platform device creation from the PCIE driver. It+provides the key features for FPGA management, including:++ a) Power and thermal management, error reporting, performance reporting+ and other infrastructure functions. Users can access these functions+ via sysfs interfaces exposed by FME driver.+ b) Partial Reconfiguration. The FME driver creates FPGA manager, FPGA+ bridges and FPGA regions during PR sub feature initialization; Once+ it receives an FPGA_FME_PORT_PR ioctl from user, it invokes the+ common interface function from FPGA Region to complete the partial+ reconfiguration of the bitstream to the given port.+ c) Port management for virtualization. The FME driver introduces two+ ioctls, FPGA_FME_PORT_RELEASE (releases given port from PF) and+ FPGA_FME_PORT_ASSIGN (assigns the port back to PF). Once the port is+ released from the PF, it can be assigned to the VF through the SRIOV+ interfaces provided by PCIE driver. (Refer to "FPGA virtualization"+ for more details).++Similar to the the FME driver, the FPGA Accelerated Function Unit (AFU) driver+is probed once the AFU platform device is created. The main function of this+module is to provide an interface for userspace applications to access the+individual accelerators, including basic reset control on port, AFU MMIO region+export, dma buffer mapping service, UMsg notification, and remote debug+functions (see above).+++Device enumeration+==================+This section introduces how applications enumerate the fpga device from+the sysfs hierarchy under /sys/class/fpga_region.++In the example below, two Intel(R) FPGA devices are installed in the host. Each+fpga device has one FME and two ports (AFUs).++FPGA regions are created under /sys/class/fpga_region/++ /sys/class/fpga_region/region0+ /sys/class/fpga_region/region1+ /sys/class/fpga_region/region2+ ...++Application needs to search each regionX folder, if feature device is found,+(e.g "fpga-dfl-port.n" or "fpga-dfl-fme.m" is found), then it's the base+fpga region which represents the FPGA device.++Each base region has one FME and two ports (AFUs) as child devices:++ /sys/class/fpga_region/region0/fpga-dfl-fme.0+ /sys/class/fpga_region/region0/fpga-dfl-port.0+ /sys/class/fpga_region/region0/fpga-dfl-port.1+ ...++ /sys/class/fpga_region/region3/fpga-dfl-fme.1+ /sys/class/fpga_region/region3/fpga-dfl-port.2+ /sys/class/fpga_region/region3/fpga-dfl-port.3+ ...++In general, the FME/AFU sysfs interfaces are named as follows:++ /sys/class/fpga_region/<regionX>/<fpga-dfl-fme.n>/+ /sys/class/fpga_region/<regionX>/<fpga-dfl-port.m>/++with 'n' consecutively numbering all FMEs and 'm' consecutively numbering all+ports.++The device nodes used for ioctl() or mmap() can be referenced through:++ /sys/class/fpga_region/<regionX>/<fpga-dfl-fme.n>/dev+ /sys/class/fpga_region/<regionX>/<fpga-dfl-port.n>/dev++Open discussion+===============+FME driver exports one ioctl (FPGA_FME_PORT_PR) for partial reconfiguration to+user now. In the future, if unified user interfaces for reconfiguration are+added, FME driver should switch to them from ioctl interface.
This patch adds status to fpga-manager data structure, to allow
driver to store full/partial reconfiguration errors and other
status information, and adds one status callback to fpga_manager_ops
to allow fpga_manager to collect latest status when failures are
detected.
The following sysfs file is created:
* /sys/class/fpga_manager/<fpga>/status
Return status of fpga manager, including reconfiguration errors.
Signed-off-by: Wu Hao <redacted>
----
v3: add one line description for status
add status callback function to fpga_manager_ops
update fpga-mgr status if any failure or during initialization
s/INCOMPATIBLE_BS_ERR/INCOMPATIBLE_IMAGE_ERR/
---
Documentation/ABI/testing/sysfs-class-fpga-manager | 10 ++++++++
drivers/fpga/fpga-mgr.c | 28 ++++++++++++++++++++++
include/linux/fpga/fpga-mgr.h | 17 +++++++++++++
3 files changed, 55 insertions(+)
@@ -35,3 +35,13 @@ Description: Read fpga manager state as a string. * write complete = Doing post programming steps * write complete error = Error while doing post programming * operating = FPGA is programmed and operating++What: /sys/class/fpga_manager/<fpga>/status+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read fpga manager status as a string.+ If FPGA programming operation fails, it could be due to crc+ error or incompatible bitstream image. The intent of this+ interface is to provide more detailed information for FPGA+ programming errors to userspace.
@@ -88,6 +88,7 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error preparing FPGA for writing\n");mgr->state=FPGA_MGR_STATE_WRITE_INIT_ERR;+fpga_mgr_update_status(mgr);returnret;}
@@ -148,6 +149,7 @@ static int fpga_mgr_write_complete(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error after writing image data to FPGA\n");mgr->state=FPGA_MGR_STATE_WRITE_COMPLETE_ERR;+fpga_mgr_update_status(mgr);returnret;}mgr->state=FPGA_MGR_STATE_OPERATING;
@@ -225,6 +227,7 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error while writing image data to FPGA\n");mgr->state=FPGA_MGR_STATE_WRITE_ERR;+fpga_mgr_update_status(mgr);returnret;}
Device Feature List (DFL) defines a feature list structure that creates
a link list of feature headers within the MMIO space to provide an
extensible way of adding features. This patch introduces a kernel module
to provide basic infrastructure to support FPGA devices which implement
the Device Feature List.
Usually there will be different features and their sub features linked into
the DFL. This code provides common APIs for feature enumeration, it creates
a container device (FPGA base region), walks through the DFLs and creates
platform devices for feature devices (Currently it only supports two
different feature devices, FPGA Management Engine (FME) and Port which
the Accelerator Function Unit (AFU) connected to). In order to enumerate
the DFLs, the common APIs required low level driver to provide necessary
enumeration information (e.g address for each device feature list for
given device) and fill it to the fpga_enum_info data structure. Please
refer to below description for APIs added for enumeration.
Functions for enumeration information preparation:
*fpga_enum_info_alloc
allocate enumeration information data structure.
*fpga_enum_info_add_dfl
add a device feature list to fpga_enum_info data structure.
*fpga_enum_info_free
free fpga_enum_info data structure and related resources.
Functions for feature device enumeration:
*fpga_enumerate_feature_devs
enumerate feature devices and return container device.
*fpga_remove_feature_devs
remove feature devices under given container device.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Zhang Yi <redacted>
Signed-off-by: Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Signed-off-by: Wu Hao <redacted>
----
v3: split from another patch.
separate dfl enumeration code from original pcie driver.
provide common data structures and APIs for enumeration.
update device feature list parsing process according to latest hw.
add dperf/iperf/hssi sub feature placeholder according to latest hw.
remove build_info_add_sub_feature and other small functions.
replace *_feature_num function with macro.
remove writeq/readq.
---
drivers/fpga/Kconfig | 16 +
drivers/fpga/Makefile | 3 +
drivers/fpga/fpga-dfl.c | 884 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/fpga-dfl.h | 365 ++++++++++++++++++++
4 files changed, 1268 insertions(+)
create mode 100644 drivers/fpga/fpga-dfl.c
create mode 100644 drivers/fpga/fpga-dfl.h
@@ -0,0 +1,884 @@+/*+*DriverforFPGADeviceFeatureList(DFL)Support+*+*Copyright(C)2017IntelCorporation,Inc.+*+*Authors:+*KangLuwei<luwei.kang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>+*ZhangYi<yi.z.zhang-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>+*WuHao<hao.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>+*XiaoGuangrong<guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>+*+*ThisworkislicensedunderthetermsoftheGNUGPLversion2.+*SPDX-License-Identifier:GPL-2.0+*/+#include<linux/module.h>++#include"fpga-dfl.h"++staticDEFINE_MUTEX(fpga_id_mutex);++enumfpga_id_type{+FME_ID,/* fme id allocation and mapping */+PORT_ID,/* port id allocation and mapping */+FPGA_ID_MAX,+};++/* it is protected by fpga_id_mutex */+staticstructidrfpga_ids[FPGA_ID_MAX];++staticvoidfpga_ids_init(void)+{+inti;++for(i=0;i<ARRAY_SIZE(fpga_ids);i++)+idr_init(fpga_ids+i);+}++staticvoidfpga_ids_destroy(void)+{+inti;++for(i=0;i<ARRAY_SIZE(fpga_ids);i++)+idr_destroy(fpga_ids+i);+}++staticintalloc_fpga_id(enumfpga_id_typetype,structdevice*dev)+{+intid;++WARN_ON(type>=FPGA_ID_MAX);+mutex_lock(&fpga_id_mutex);+id=idr_alloc(fpga_ids+type,dev,0,0,GFP_KERNEL);+mutex_unlock(&fpga_id_mutex);++returnid;+}++staticvoidfree_fpga_id(enumfpga_id_typetype,intid)+{+WARN_ON(type>=FPGA_ID_MAX);+mutex_lock(&fpga_id_mutex);+idr_remove(fpga_ids+type,id);+mutex_unlock(&fpga_id_mutex);+}++staticenumfpga_id_typefeature_dev_id_type(structplatform_device*pdev)+{+if(!strcmp(pdev->name,FPGA_FEATURE_DEV_FME))+returnFME_ID;++if(!strcmp(pdev->name,FPGA_FEATURE_DEV_PORT))+returnPORT_ID;++WARN_ON(1);++returnFPGA_ID_MAX;+}++/**+*build_feature_devs_info-infocollectedduringfeaturedevbuild.+*+*@dev:devicetoenumerate.+*@cdev:thecontainerdeviceforallfeaturedevices.+*@feature_dev:currentfeaturedevice.+*/+structbuild_feature_devs_info{+structdevice*dev;+structfpga_cdev*cdev;+structplatform_device*feature_dev;+};++staticvoidfpga_cdev_add_port_dev(structfpga_cdev*cdev,+structplatform_device*port_pdev)+{+structfeature_platform_data*pdata=dev_get_platdata(&port_pdev->dev);++mutex_lock(&cdev->lock);+list_add(&pdata->node,&cdev->port_dev_list);+get_device(&pdata->dev->dev);+mutex_unlock(&cdev->lock);+}++/*+*registercurrentfeaturedevice,itiscalledwhenweneedtoswitchto+*anotherfeatureparsingorwehaveparsedallfeaturesongivendevice+*featurelist.+*/+staticintbuild_info_commit_dev(structbuild_feature_devs_info*binfo)+{+intret;++if(!binfo->feature_dev)+return0;++ret=platform_device_add(binfo->feature_dev);+if(!ret){+if(feature_dev_id_type(binfo->feature_dev)==PORT_ID)+fpga_cdev_add_port_dev(binfo->cdev,binfo->feature_dev);+else+binfo->cdev->fme_dev=+get_device(&binfo->feature_dev->dev);+/*+*resetittoavoidbuild_info_free()freeingtheirresource.+*+*Theresourceofsuccessfullyregisteredfeaturedevices+*willbefreedbyplatform_device_unregister().Seethe+*commentsinbuild_info_create_dev().+*/+binfo->feature_dev=NULL;+}++returnret;+}++staticint+build_info_create_dev(structbuild_feature_devs_info*binfo,+enumfpga_id_typetype,intfeature_nr,constchar*name)+{+structplatform_device*fdev;+structresource*res;+structfeature_platform_data*pdata;+intret;++/* we will create a new device, commit current device first */+ret=build_info_commit_dev(binfo);+if(ret)+returnret;++/*+*weuse-ENODEVastheinitializationindicatorwhichindicates+*whethertheidneedtobereclaimed+*/+fdev=platform_device_alloc(name,-ENODEV);+if(!fdev)+return-ENOMEM;++binfo->feature_dev=fdev;++fdev->id=alloc_fpga_id(type,&fdev->dev);+if(fdev->id<0)+returnfdev->id;++fdev->dev.parent=&binfo->cdev->region.dev;++/*+*wedonotneedtocareforthememorywhichisassociatedwith+*theplatformdevice.Aftercallingplatform_device_unregister(),+*itwillbeautomaticallyfreedbydevice'srelease()callback,+*platform_device_release().+*/+pdata=kzalloc(feature_platform_data_size(feature_nr),GFP_KERNEL);+if(pdata){+pdata->dev=fdev;+pdata->num=feature_nr;+mutex_init(&pdata->lock);+}else{+return-ENOMEM;+}++/*+*thecountshouldbeinitializedto0tomakesure+*__fpga_port_enable()following__fpga_port_disable()+*worksproperlyforportdevice.+*anditshouldalwaysbe0forfmedevice.+*/+WARN_ON(pdata->disable_count);++fdev->dev.platform_data=pdata;+fdev->num_resources=feature_nr;+fdev->resource=kcalloc(feature_nr,sizeof(*res),GFP_KERNEL);+if(!fdev->resource)+return-ENOMEM;++return0;+}++staticvoidbuild_info_free(structbuild_feature_devs_info*binfo)+{+/*+*itisavalidid,freeit.Seecommentsin+*build_info_create_dev()+*/+if(binfo->feature_dev&&binfo->feature_dev->id>=0)+free_fpga_id(feature_dev_id_type(binfo->feature_dev),+binfo->feature_dev->id);++platform_device_put(binfo->feature_dev);++devm_kfree(binfo->dev,binfo);+}++/*+*UAFUGUIDisdynamicasitcanbechangedafterFMEdownloadsdifferent+*GreenBitstreamtotheport,sowetreattheunknownGUIDswhichare+*attachedonport'sfeaturelistasUAFU.+*/+staticboolfeature_is_UAFU(structbuild_feature_devs_info*binfo)+{+if(!binfo->feature_dev||+feature_dev_id_type(binfo->feature_dev)!=PORT_ID)+returnfalse;++returntrue;+}++structfeature_info{+constchar*name;+resource_size_tresource_size;+intfeature_index;+};++/* indexed by fme feature IDs which are defined in 'enum fme_feature_id'. */+staticstructfeature_infofme_features[]={+{+.name=FME_FEATURE_HEADER,+.resource_size=FME_HDR_SIZE,+.feature_index=FME_FEATURE_ID_HEADER,+},+{+.name=FME_FEATURE_THERMAL_MGMT,+.resource_size=FME_THERMAL_SIZE,+.feature_index=FME_FEATURE_ID_THERMAL_MGMT,+},+{+.name=FME_FEATURE_POWER_MGMT,+.resource_size=FME_POWER_SIZE,+.feature_index=FME_FEATURE_ID_POWER_MGMT,+},+{+.name=FME_FEATURE_GLOBAL_IPERF,+.resource_size=FME_IPERF_SIZE,+.feature_index=FME_FEATURE_ID_GLOBAL_IPERF,+},+{+.name=FME_FEATURE_GLOBAL_ERR,+.resource_size=FME_ERR_SIZE,+.feature_index=FME_FEATURE_ID_GLOBAL_ERR,+},+{+.name=FME_FEATURE_PR_MGMT,+.resource_size=FME_PR_SIZE,+.feature_index=FME_FEATURE_ID_PR_MGMT,+},+{+.name=FME_FEATURE_HSSI,+.resource_size=FME_HSSI_SIZE,+.feature_index=FME_FEATURE_ID_HSSI,+},+{+.name=FME_FEATURE_GLOBAL_DPERF,+.resource_size=FME_DPERF_SIZE,+.feature_index=FME_FEATURE_ID_GLOBAL_DPERF,+},+};++/* indexed by port feature IDs which are defined in 'enum port_feature_id'. */+staticstructfeature_infoport_features[]={+{+.name=PORT_FEATURE_HEADER,+.resource_size=PORT_HDR_SIZE,+.feature_index=PORT_FEATURE_ID_HEADER,+},+{+.name=PORT_FEATURE_ERR,+.resource_size=PORT_ERR_SIZE,+.feature_index=PORT_FEATURE_ID_ERROR,+},+{+.name=PORT_FEATURE_UMSG,+.resource_size=PORT_UMSG_SIZE,+.feature_index=PORT_FEATURE_ID_UMSG,+},+{+/* This feature isn't available for now */+.name=PORT_FEATURE_PR,+.resource_size=DFH_SIZE,+.feature_index=PORT_FEATURE_ID_PR,+},+{+.name=PORT_FEATURE_STP,+.resource_size=PORT_STP_SIZE,+.feature_index=PORT_FEATURE_ID_STP,+},+{+/*+*ForUserAFUfeature,itsregionsizeisnotfixed,but+*reportedbyregisterPortCapability.mmio_size.Resource+*sizeofUAFUwillbesetwhileparseportdevice.+*/+.name=PORT_FEATURE_UAFU,+.resource_size=0,+.feature_index=PORT_FEATURE_ID_UAFU,+},+};++staticint+create_feature_instance(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,resource_size_tofst,+structfeature_info*finfo)+{+intindex=finfo->feature_index;+structplatform_device*fdev=binfo->feature_dev;+structfeature_platform_data*pdata=dev_get_platdata(&fdev->dev);+structresource*res=&fdev->resource[index];++if((dfl->len-ofst<finfo->resource_size)||pdata->num<index)+return-EINVAL;++res->start=dfl->start+ofst;+res->end=res->start+finfo->resource_size-1;+res->flags=IORESOURCE_MEM;+res->name=finfo->name;++pdata->features[index].name=finfo->name;+pdata->features[index].resource_index=index;+pdata->features[index].ioaddr=dfl->ioaddr+ofst;++return0;+}++staticintparse_feature_fme(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+intret;++ret=build_info_create_dev(binfo,FME_ID,FME_FEATURE_NUM,+FPGA_FEATURE_DEV_FME);+if(ret)+returnret;++returncreate_feature_instance(binfo,dfl,ofst,+&fme_features[FME_FEATURE_ID_HEADER]);+}++staticintparse_feature_fme_private(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+u64v;+intid;++v=readq(dfl->ioaddr+ofst+DFH);+id=FIELD_GET(DFH_ID,v);++if(id>=ARRAY_SIZE(fme_features)){+dev_info(binfo->dev,"FME feature id %x is not supported yet.\n",+id);+return0;+}++returncreate_feature_instance(binfo,dfl,ofst,&fme_features[id]);+}++staticintparse_feature_port(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+intret;++ret=build_info_create_dev(binfo,PORT_ID,PORT_FEATURE_NUM,+FPGA_FEATURE_DEV_PORT);+if(ret)+returnret;++returncreate_feature_instance(binfo,dfl,ofst,+&port_features[PORT_FEATURE_ID_HEADER]);+}++staticvoidenable_port_uafu(structbuild_feature_devs_info*binfo)+{+enumport_feature_idid=PORT_FEATURE_ID_UAFU;+void__iomem*base;+u64v;++base=get_feature_ioaddr_by_index(&binfo->feature_dev->dev,+PORT_FEATURE_ID_HEADER);++v=readq(base+PORT_HDR_CAP);+port_features[id].resource_size=+FIELD_GET(PORT_CAP_MMIO_SIZE,v)<<10;++/*+*ToenableUserAFU,driverneedstoclearresetbitonrelatedport,+*otherwisethemmiospaceofthisuserAFUwillbeinvalid.+*/+if(port_features[id].resource_size)+fpga_port_reset(binfo->feature_dev);+}++staticintparse_feature_port_private(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+enumport_feature_idid;+u32dfh_id;+u64v;++v=readq(dfl->ioaddr+ofst+DFH);+dfh_id=FIELD_GET(DFH_ID,v);++/*+*theregionofportfeatureidis[0x10,0x13],+1toreserve0+*whichisdedicatedforport-hdr.+*/+id=(dfh_id&0x000f)+1;++if(id>=ARRAY_SIZE(port_features)){+dev_info(binfo->dev,"Port feature id %x is not supported yet.\n",+dfh_id);+return0;+}++returncreate_feature_instance(binfo,dfl,ofst,&port_features[id]);+}++staticintparse_feature_port_uafu(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+enumport_feature_idid=PORT_FEATURE_ID_UAFU;+intret;++if(port_features[id].resource_size){+ret=create_feature_instance(binfo,dfl,ofst,+&port_features[id]);+port_features[id].resource_size=0;+}else{+dev_err(binfo->dev,"the uafu feature header is mis-configured.\n");+ret=-EINVAL;+}++returnret;+}++staticintparse_feature_afus(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+void__iomem*start=dfl->ioaddr+ofst;+void__iomem*end=dfl->ioaddr+dfl->len;+u32offset;+u64v;+intret;++for(;start<end;start+=offset){+if(end-start<AFU_DFH_SIZE)+return-EINVAL;++if(feature_is_UAFU(binfo))+ret=parse_feature_port_uafu(binfo,dfl,+start-dfl->ioaddr);+if(ret)+returnret;++v=readq(start+NEXT_AFU);++offset=FIELD_GET(NEXT_AFU_NEXT_DFH_OFST,v);+if(!offset)+break;+}++return0;+}++staticintparse_feature_fiu(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+u32id,offset;+u64v;+intret=0;++v=readq(dfl->ioaddr+ofst+DFH);+id=FIELD_GET(DFH_ID,v);++switch(id){+caseDFH_ID_FIU_FME:+returnparse_feature_fme(binfo,dfl,ofst);+caseDFH_ID_FIU_PORT:+ret=parse_feature_port(binfo,dfl,ofst);+enable_port_uafu(binfo);+if(ret)+returnret;++/* Check Port FIU's next_afu pointer to User AFU DFH */+v=readq(dfl->ioaddr+ofst+NEXT_AFU);++offset=FIELD_GET(NEXT_AFU_NEXT_DFH_OFST,v);+if(offset)+returnparse_feature_afus(binfo,dfl,ofst+offset);++dev_dbg(binfo->dev,"No AFUs detected on Port\n");+break;+default:+dev_info(binfo->dev,"FIU TYPE %d is not supported yet.\n",+id);+}++returnret;+}++staticintparse_feature_private(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,+resource_size_tofst)+{+u64v;+u32id;++v=readq(dfl->ioaddr+ofst+DFH);+id=FIELD_GET(DFH_ID,v);++if(!binfo->feature_dev){+dev_err(binfo->dev,"the private feature %x does not belong to any AFU.\n",+id);+return-EINVAL;+}++switch(feature_dev_id_type(binfo->feature_dev)){+caseFME_ID:+returnparse_feature_fme_private(binfo,dfl,ofst);+casePORT_ID:+returnparse_feature_port_private(binfo,dfl,ofst);+default:+dev_info(binfo->dev,"private feature %x belonging to AFU %s is not supported yet.\n",+id,binfo->feature_dev->name);+}+return0;+}++/**+*parse_feature-parseafeatureongivendevicefeaturelist+*+*@binfo:buildfeaturedevicesinformation.+*@dfl:devicefeaturelisttoparse+*@ofst:offsettofeatureheaderonthisdevicefeaturelist+*/+staticintparse_feature(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl,resource_size_tofst)+{+u64v;+u32type;++v=readq(dfl->ioaddr+ofst+DFH);+type=FIELD_GET(DFH_TYPE,v);++switch(type){+caseDFH_TYPE_AFU:+returnparse_feature_afus(binfo,dfl,ofst);+caseDFH_TYPE_PRIVATE:+returnparse_feature_private(binfo,dfl,ofst);+caseDFH_TYPE_FIU:+returnparse_feature_fiu(binfo,dfl,ofst);+default:+dev_info(binfo->dev,+"Feature Type %x is not supported.\n",type);+}++return0;+}++staticintparse_feature_list(structbuild_feature_devs_info*binfo,+structfpga_enum_dfl*dfl)+{+void__iomem*start=dfl->ioaddr;+void__iomem*end=dfl->ioaddr+dfl->len;+intret=0;+u32ofst=0;+u64v;++/* walk through the device feature list via DFH's next DFH pointer. */+for(;start<end;start+=ofst){+if(end-start<DFH_SIZE){+dev_err(binfo->dev,"The region is too small to contain a feature.\n");+return-EINVAL;+}++ret=parse_feature(binfo,dfl,start-dfl->ioaddr);+if(ret)+returnret;++v=readq(start+DFH);+ofst=FIELD_GET(DFH_NEXT_HDR_OFST,v);++/* stop parsing if EOL(End of List) is set or offset is 0 */+if((v&DFH_EOL)||!ofst)+break;+}++/* commit current feature device when reach the end of list */+returnbuild_info_commit_dev(binfo);+}++structfpga_enum_info*fpga_enum_info_alloc(structdevice*dev)+{+structfpga_enum_info*info;++get_device(dev);++info=devm_kzalloc(dev,sizeof(*info),GFP_KERNEL);+if(!info){+put_device(dev);+returnNULL;+}++info->dev=dev;+INIT_LIST_HEAD(&info->dfls);++returninfo;+}+EXPORT_SYMBOL_GPL(fpga_enum_info_alloc);++intfpga_enum_info_add_dfl(structfpga_enum_info*info,resource_size_tstart,+resource_size_tlen,void__iomem*ioaddr)+{+structfpga_enum_dfl*dfl;++dfl=devm_kzalloc(info->dev,sizeof(*dfl),GFP_KERNEL);+if(!dfl)+return-ENOMEM;++dfl->start=start;+dfl->len=len;+dfl->ioaddr=ioaddr;++list_add_tail(&dfl->node,&info->dfls);++return0;+}+EXPORT_SYMBOL_GPL(fpga_enum_info_add_dfl);++voidfpga_enum_info_free(structfpga_enum_info*info)+{+structfpga_enum_dfl*tmp,*dfl;+structdevice*dev;++if(!info)+return;++dev=info->dev;++/* remove all device feature lists in the list. */+list_for_each_entry_safe(dfl,tmp,&info->dfls,node){+list_del(&dfl->node);+devm_kfree(dev,dfl);+}++devm_kfree(dev,info);+put_device(dev);+}+EXPORT_SYMBOL_GPL(fpga_enum_info_free);++staticintremove_feature_dev(structdevice*dev,void*data)+{+structplatform_device*pdev=to_platform_device(dev);+enumfpga_id_typetype=feature_dev_id_type(pdev);+intid=pdev->id;++platform_device_unregister(pdev);++free_fpga_id(type,id);++return0;+}++staticvoidremove_feature_devs(structfpga_cdev*cdev)+{+device_for_each_child(&cdev->region.dev,NULL,remove_feature_dev);+}++/**+*fpga_enumerate_feature_devs-enumeratefeaturedevices+*@info:informationforenumeration.+*+*Thisfunctioncreatesacontainerdevice(baseFPGAregion),enumerates+*featuredevicesbasedontheenumerationinfoandcreatesplatformdevices+*underthecontainerdevice.+*+*Return:fpga_cdevstructonsuccess,-errnoonfailure+*/+structfpga_cdev*fpga_enumerate_feature_devs(structfpga_enum_info*info)+{+structbuild_feature_devs_info*binfo;+structfpga_cdev*cdev;+structfpga_enum_dfl*dfl;+intret=0;++if(!info->dev)+returnERR_PTR(-ENODEV);++cdev=devm_kzalloc(info->dev,sizeof(*cdev),GFP_KERNEL);+if(!cdev)+returnERR_PTR(-ENOMEM);++cdev->parent=info->dev;+mutex_init(&cdev->lock);+INIT_LIST_HEAD(&cdev->port_dev_list);+cdev->region.parent=info->dev;++ret=fpga_region_register(&cdev->region);+if(ret)+gotofree_cdev_exit;++/* create and init build info for enumeration */+binfo=devm_kzalloc(info->dev,sizeof(*binfo),GFP_KERNEL);+if(!binfo){+ret=-ENOMEM;+gotounregister_region_exit;+}++binfo->dev=info->dev;+binfo->cdev=cdev;++/*+*startenumerationforallfeaturedevicesbasedonDeviceFeature+*Lists.+*/+list_for_each_entry(dfl,&info->dfls,node){+ret=parse_feature_list(binfo,dfl);+if(ret){+remove_feature_devs(cdev);+build_info_free(binfo);+gotounregister_region_exit;+}+}++build_info_free(binfo);++returncdev;++unregister_region_exit:+fpga_region_unregister(&cdev->region);+free_cdev_exit:+devm_kfree(cdev->parent,cdev);+returnERR_PTR(ret);+}+EXPORT_SYMBOL_GPL(fpga_enumerate_feature_devs);++/**+*fpga_remove_feature_devs-removeallfeaturedevices+*@cdev:fpgacontainerdevice.+*+*Removethecontainerdeviceandallfeaturedevicesundergivencontainer+*devices.+*/+voidfpga_remove_feature_devs(structfpga_cdev*cdev)+{+structfeature_platform_data*pdata,*ptmp;++remove_feature_devs(cdev);++mutex_lock(&cdev->lock);+if(cdev->fme_dev){+/* the fme should be unregistered. */+WARN_ON(device_is_registered(cdev->fme_dev));+put_device(cdev->fme_dev);+}++list_for_each_entry_safe(pdata,ptmp,&cdev->port_dev_list,node){+structplatform_device*port_dev=pdata->dev;++/* the port should be unregistered. */+WARN_ON(device_is_registered(&port_dev->dev));+list_del(&pdata->node);+put_device(&port_dev->dev);+}+mutex_unlock(&cdev->lock);++fpga_region_unregister(&cdev->region);+devm_kfree(cdev->parent,cdev);+}+EXPORT_SYMBOL_GPL(fpga_remove_feature_devs);++intfpga_port_id(structplatform_device*pdev)+{+void__iomem*base;++base=get_feature_ioaddr_by_index(&pdev->dev,PORT_FEATURE_ID_HEADER);+WARN_ON(!base);++returnFIELD_GET(PORT_CAP_PORT_NUM,readq(base+FME_HDR_CAP));+}+EXPORT_SYMBOL_GPL(fpga_port_id);++/*+*EnablePortbycleartheportsoftresetbit,whichissetbydefault.+*TheUserAFUisunabletorespondtoanyMMIOaccesswhileinreset.+*__fpga_port_enablefunctionshouldonlybeusedafter__fpga_port_disable+*function.+*/+void__fpga_port_enable(structplatform_device*pdev)+{+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+void__iomem*base;+u64v;++WARN_ON(!pdata->disable_count);++if(--pdata->disable_count!=0)+return;++base=get_feature_ioaddr_by_index(&pdev->dev,PORT_FEATURE_ID_HEADER);+WARN_ON(!base);++/* Clear port soft reset */+v=readq(base+PORT_HDR_CTRL);+v&=~PORT_CTRL_SFTRST;+writeq(v,base+PORT_HDR_CTRL);+}+EXPORT_SYMBOL_GPL(__fpga_port_enable);++#define RST_POLL_INVL 10 /* us */+#define RST_POLL_TIMEOUT 1000 /* us */++int__fpga_port_disable(structplatform_device*pdev)+{+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+void__iomem*base;+u64v;++if(pdata->disable_count++!=0)+return0;++base=get_feature_ioaddr_by_index(&pdev->dev,PORT_FEATURE_ID_HEADER);+WARN_ON(!base);++/* Set port soft reset */+v=readq(base+PORT_HDR_CTRL);+v|=PORT_CTRL_SFTRST;+writeq(v,base+PORT_HDR_CTRL);++/*+*HWsetsackbitto1whenalloutstandingrequestshavebeendrained+*onthisportandminimumsoftresetpulsewidthhaselapsed.+*Driverpollsport_soft_reset_acktodetermineifresetdonebyHW.+*/+if(readq_poll_timeout(base+PORT_HDR_CTRL,v,v&PORT_CTRL_SFTRST,+RST_POLL_INVL,RST_POLL_TIMEOUT)){+dev_err(&pdev->dev,"timeout, fail to reset device\n");+return-ETIMEDOUT;+}++return0;+}+EXPORT_SYMBOL_GPL(__fpga_port_disable);++staticint__initdfl_fpga_init(void)+{+fpga_ids_init();++return0;+}++staticvoid__exitdfl_fpga_exit(void)+{+fpga_ids_destroy();+}++module_init(dfl_fpga_init);+module_exit(dfl_fpga_exit);++MODULE_DESCRIPTION("FPGA Device Feature List (DFL) Support");+MODULE_AUTHOR("Intel Corporation");+MODULE_LICENSE("GPL v2");
For feature devices drivers, both the FPGA Management Engine (FME) and
Accelerated Function Unit (AFU) driver need to expose user interfaces via
the device file, for example, mmap and ioctls.
This patch adds chardev support in the fpga-dfl driver for feature devices,
FME and AFU. It reserves the chardev regions for FME and AFU, and provide
interfaces for FME and AFU driver to register their device file operations.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Zhang Yi <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: rebased
v3: move chardev support to fpga-dfl framework
---
drivers/fpga/fpga-dfl.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++-
drivers/fpga/fpga-dfl.h | 13 +++++++
2 files changed, 103 insertions(+), 1 deletion(-)
@@ -139,8 +216,12 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)structplatform_device*fdev;structresource*res;structfeature_platform_data*pdata;+enumfpga_devt_typedevt_type=FPGA_DEVT_FME;intret;+if(type==PORT_ID)+devt_type=FPGA_DEVT_PORT;+/* we will create a new device, commit current device first */ret=build_info_commit_dev(binfo);if(ret)
@@ -161,6 +242,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)returnfdev->id;fdev->dev.parent=&binfo->cdev->region.dev;+fdev->dev.devt=fpga_get_devt(devt_type,fdev->id);/**wedonotneedtocareforthememorywhichisassociatedwith
@@ -866,13 +948,20 @@ int __fpga_port_disable(struct platform_device *pdev)staticint__initdfl_fpga_init(void){+intret;+fpga_ids_init();-return0;+ret=fpga_chardev_init();+if(ret)+fpga_ids_destroy();++returnret;}staticvoid__exitdfl_fpga_exit(void){+fpga_chardev_uinit();fpga_ids_destroy();}
@@ -185,6 +186,7 @@ struct feature_platform_data {/* list the feature dev to cci_drvdata->port_dev_list. */structlist_headnode;structmutexlock;/* protect platform data */+structcdevcdev;structplatform_device*dev;unsignedintdisable_count;/* count for port disable */
@@ -226,6 +228,17 @@ static inline int feature_platform_data_size(const int num)num*sizeof(structfeature);}+enumfpga_devt_type{+FPGA_DEVT_FME,+FPGA_DEVT_PORT,+FPGA_DEVT_MAX,+};++intfpga_register_dev_ops(structplatform_device*pdev,+conststructfile_operations*fops,+structmodule*owner);+voidfpga_unregister_dev_ops(structplatform_device*pdev);+intfpga_port_id(structplatform_device*pdev);staticinlineintfpga_port_check_id(structplatform_device*pdev,
From: Xiao Guangrong <redacted>
This patch abstracts the common operations of the sub features, and defines
the feature_ops data structure, including init, uinit and ioctl function
pointers. And this patch adds some common helper functions for FME and AFU
drivers, e.g feature_dev_use_begin/end which are used to ensure exclusive
usage of the feature device file.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Kang Luwei <redacted>
Signed-off-by: Zhang Yi <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: rebased
v3: use const for feature_ops.
replace pci related function.
---
drivers/fpga/fpga-dfl.c | 68 +++++++++++++++++++++++++++++++++++++++++
drivers/fpga/fpga-dfl.h | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+)
@@ -76,6 +76,74 @@ static enum fpga_id_type feature_dev_id_type(struct platform_device *pdev)returnFPGA_ID_MAX;}+intfme_feature_to_resource_index(intfeature_id)+{+WARN_ON(feature_id>=FME_FEATURE_ID_MAX);+returnfeature_id;+}++voidfpga_dev_feature_uinit(structplatform_device*pdev)+{+structfeature*feature;+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);++fpga_dev_for_each_feature(pdata,feature)+if(feature->ops){+feature->ops->uinit(pdev,feature);+feature->ops=NULL;+}+}+EXPORT_SYMBOL_GPL(fpga_dev_feature_uinit);++staticint+feature_instance_init(structplatform_device*pdev,+structfeature_platform_data*pdata,+structfeature*feature,structfeature_driver*drv)+{+intret;++WARN_ON(!feature->ioaddr);++ret=drv->ops->init(pdev,feature);+if(ret)+returnret;++feature->ops=drv->ops;++returnret;+}++intfpga_dev_feature_init(structplatform_device*pdev,+structfeature_driver*feature_drvs)+{+structfeature*feature;+structfeature_driver*drv=feature_drvs;+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+intret;++while(drv->ops){+fpga_dev_for_each_feature(pdata,feature){+/* skip the feature which is not initialized. */+if(!feature->name)+continue;++if(!strcmp(drv->name,feature->name)){+ret=feature_instance_init(pdev,pdata,+feature,drv);+if(ret)+gotoexit;+}+}+drv++;+}++return0;+exit:+fpga_dev_feature_uinit(pdev);+returnret;+}+EXPORT_SYMBOL_GPL(fpga_dev_feature_init);+structfpga_chardev_info{constchar*name;dev_tdevt;
@@ -176,12 +176,20 @@#define PORT_UAFU_DFH DFH#define PORT_UAFU_SIZE DFH_SIZE+structfeature_driver{+constchar*name;+conststructfeature_ops*ops;+};+structfeature{constchar*name;intresource_index;void__iomem*ioaddr;+conststructfeature_ops*ops;};+#define DEV_STATUS_IN_USE 0+structfeature_platform_data{/* list the feature dev to cci_drvdata->port_dev_list. */structlist_headnode;
@@ -189,11 +197,46 @@ struct feature_platform_data {structcdevcdev;structplatform_device*dev;unsignedintdisable_count;/* count for port disable */+unsignedlongdev_status;++void*private;/* ptr to feature dev private data */intnum;/* number of features */structfeaturefeatures[0];};+staticinlineintfeature_dev_use_begin(structfeature_platform_data*pdata)+{+/* Test and set IN_USE flags to ensure file is exclusively used */+if(test_and_set_bit_lock(DEV_STATUS_IN_USE,&pdata->dev_status))+return-EBUSY;++return0;+}++staticinlinevoidfeature_dev_use_end(structfeature_platform_data*pdata)+{+clear_bit_unlock(DEV_STATUS_IN_USE,&pdata->dev_status);+}++staticinlinevoid+fpga_pdata_set_private(structfeature_platform_data*pdata,void*private)+{+pdata->private=private;+}++staticinlinevoid*fpga_pdata_get_private(structfeature_platform_data*pdata)+{+returnpdata->private;+}++structfeature_ops{+int(*init)(structplatform_device*pdev,structfeature*feature);+void(*uinit)(structplatform_device*pdev,structfeature*feature);+long(*ioctl)(structplatform_device*pdev,structfeature*feature,+unsignedintcmd,unsignedlongarg);+};+enumfme_feature_id{FME_FEATURE_ID_HEADER=0x0,FME_FEATURE_ID_THERMAL_MGMT=0x1,
@@ -228,6 +271,10 @@ static inline int feature_platform_data_size(const int num)num*sizeof(structfeature);}+voidfpga_dev_feature_uinit(structplatform_device*pdev);+intfpga_dev_feature_init(structplatform_device*pdev,+structfeature_driver*feature_drvs);+enumfpga_devt_type{FPGA_DEVT_FME,FPGA_DEVT_PORT,
@@ -296,6 +343,15 @@ static inline int fpga_port_reset(struct platform_device *pdev)returnret;}+staticinline+structplatform_device*fpga_inode_to_feature_dev(structinode*inode)+{+structfeature_platform_data*pdata;++pdata=container_of(inode->i_cdev,structfeature_platform_data,cdev);+returnpdata->dev;+}+staticinlinevoid__iomem*get_feature_ioaddr_by_index(structdevice*dev,intindex){
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Zhang Yi <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: move the code to drivers/fpga folder as suggested by Alan Tull.
switch to GPLv2 license.
fix comments from Moritz Fischer.
v3: switch to pci_set_dma_mask/consistent_dma_mask() function.
remove pci_save_state() in probe function.
rename driver to INTEL_FPGA_DFL_PCI and intel-dfl-pci.c to indicate
this driver supports Intel FPGA PCI devices which implement DFL.
improve Kconfig description for INTEL_FPGA_DFL_PCI
---
drivers/fpga/Kconfig | 18 ++++++
drivers/fpga/Makefile | 3 +
drivers/fpga/intel-dfl-pci.c | 129 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 150 insertions(+)
create mode 100644 drivers/fpga/intel-dfl-pci.c
The Device Feature List (DFL) is implemented in MMIO, and features
are linked via the DFLs. This patch enables pcie driver to prepare
enumeration information (e.g locations of all device feature lists
in MMIO) and use common APIs provided by the Device Feature List
framework to enumerate each feature device linked.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Zhang Yi <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v3: split from another patch
use common functions from DFL framework for enumeration.
---
drivers/fpga/intel-dfl-pci.c | 199 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 197 insertions(+), 2 deletions(-)
@@ -24,9 +24,52 @@#include<linux/errno.h>#include<linux/aer.h>+#include"fpga-dfl.h"+#define DRV_VERSION "0.8"#define DRV_NAME "intel-dfl-pci"+structcci_drvdata{+structfpga_cdev*cdev;/* container device */+structlist_headregions;/* list of pci bar mapping region */+};++/* pci bar mapping info */+structcci_region{+intbar;+void__iomem*ioaddr;/* pointer to mapped bar region */+structlist_headnode;+};++staticvoid__iomem*cci_pci_ioremap_bar(structpci_dev*pcidev,intbar)+{+structcci_drvdata*drvdata=pci_get_drvdata(pcidev);+structcci_region*region;++list_for_each_entry(region,&drvdata->regions,node)+if(region->bar==bar){+dev_dbg(&pcidev->dev,"BAR %d region exists\n",bar);+returnregion->ioaddr;+}++region=devm_kzalloc(&pcidev->dev,sizeof(*region),GFP_KERNEL);+if(!region)+returnNULL;++region->bar=bar;+region->ioaddr=pci_ioremap_bar(pcidev,bar);+if(!region->ioaddr){+dev_err(&pcidev->dev,"can't ioremap memory from BAR %d.\n",+bar);+devm_kfree(&pcidev->dev,region);+returnNULL;+}++list_add(®ion->node,&drvdata->regions);++returnregion->ioaddr;+}+/* PCI Device ID */#define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD#define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0
@@ -47,6 +90,143 @@};MODULE_DEVICE_TABLE(pci,cci_pcie_id_tbl);+staticintcci_init_drvdata(structpci_dev*pcidev)+{+structcci_drvdata*drvdata;++drvdata=devm_kzalloc(&pcidev->dev,sizeof(*drvdata),GFP_KERNEL);+if(!drvdata)+return-ENOMEM;++INIT_LIST_HEAD(&drvdata->regions);++pci_set_drvdata(pcidev,drvdata);++return0;+}++staticvoidcci_pci_release_regions(structpci_dev*pcidev)+{+structcci_drvdata*drvdata=pci_get_drvdata(pcidev);+structcci_region*tmp,*region;++list_for_each_entry_safe(region,tmp,&drvdata->regions,node){+list_del(®ion->node);+if(region->ioaddr)+pci_iounmap(pcidev,region->ioaddr);+devm_kfree(&pcidev->dev,region);+}+}++staticvoidcci_remove_drvdata(structpci_dev*pcidev)+{+structcci_drvdata*drvdata=pci_get_drvdata(pcidev);++cci_pci_release_regions(pcidev);+pci_set_drvdata(pcidev,NULL);+devm_kfree(&pcidev->dev,drvdata);+}++staticvoidcci_remove_feature_devs(structpci_dev*pcidev)+{+structcci_drvdata*drvdata=pci_get_drvdata(pcidev);++/* remove all children feature devices */+fpga_remove_feature_devs(drvdata->cdev);+}++/* enumerate feature devices under pci device */+staticintcci_enumerate_feature_devs(structpci_dev*pcidev)+{+structcci_drvdata*drvdata=pci_get_drvdata(pcidev);+structfpga_cdev*cdev;+structfpga_enum_info*info;+resource_size_tstart,len;+void__iomem*base;+intport_num,bar,i,ret=0;+u32offset;+u64v;++/* allocate enumeration info via pci_dev */+info=fpga_enum_info_alloc(&pcidev->dev);+if(!info)+return-ENOMEM;++/* start to find Device Feature List from Bar 0 */+base=cci_pci_ioremap_bar(pcidev,0);+if(!base){+ret=-ENOMEM;+gotoenum_info_free_exit;+}++/*+*PFdevicehasFMEandPorts/AFUs,andVFdeviceonlyhas1Port/AFU.+*checkthemandaddrelated"Device Feature List"infoforthenext+*stepenumeration.+*/+if(feature_is_fme(base)){+start=pci_resource_start(pcidev,0);+len=pci_resource_len(pcidev,0);++fpga_enum_info_add_dfl(info,start,len,base);++/*+*findmoreDeviceFeatureLists(e.gPorts)perinformation+*indicatedbyFMEmodule.+*/+v=readq(base+FME_HDR_CAP);+port_num=FIELD_GET(FME_CAP_NUM_PORTS,v);++WARN_ON(port_num>MAX_FPGA_PORT_NUM);++for(i=0;i<port_num;i++){+v=readq(base+FME_HDR_PORT_OFST(i));++/* skip ports which are not implemented. */+if(!(v&FME_PORT_OFST_IMP))+continue;++/*+*addPort'sDeviceFeatureListinformationfornext+*stepenumeration.+*/+bar=FIELD_GET(FME_PORT_OFST_BAR_ID,v);+offset=FIELD_GET(FME_PORT_OFST_DFH_OFST,v);+base=cci_pci_ioremap_bar(pcidev,bar);+if(!base)+continue;++start=pci_resource_start(pcidev,bar)+offset;+len=pci_resource_len(pcidev,bar)-offset;++fpga_enum_info_add_dfl(info,start,len,base+offset);+}+}elseif(feature_is_port(base)){+start=pci_resource_start(pcidev,0);+len=pci_resource_len(pcidev,0);++fpga_enum_info_add_dfl(info,start,len,base);+}else{+ret=-ENODEV;+gotoenum_info_free_exit;+}++/* start enumeration with prepared enumeration information */+cdev=fpga_enumerate_feature_devs(info);+if(IS_ERR(cdev)){+dev_err(&pcidev->dev,"Enumeration failure\n");+ret=PTR_ERR(cdev);+gotoenum_info_free_exit;+}++drvdata->cdev=cdev;++enum_info_free_exit:+fpga_enum_info_free(info);++returnret;+}+staticintcci_pci_probe(structpci_dev*pcidev,conststructpci_device_id*pcidevid){
@@ -84,9 +264,22 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)gotorelease_region_exit;}-/* TODO: create and add the platform device per feature list */-return0;+ret=cci_init_drvdata(pcidev);+if(ret){+dev_err(&pcidev->dev,"Fail to init drvdata %d.\n",ret);+gotorelease_region_exit;+}++ret=cci_enumerate_feature_devs(pcidev);+if(ret){+dev_err(&pcidev->dev,"enumeration failure %d.\n",ret);+gotoremove_drvdata_exit;+}++returnret;+remove_drvdata_exit:+cci_remove_drvdata(pcidev);release_region_exit:pci_release_regions(pcidev);disable_error_report_exit:
From: Kang Luwei <redacted>
The FPGA Management Engine (FME) provides power, thermal management,
performance counters, partial reconfiguration and other functions. For each
function, it is packaged into a private feature linked to the FME feature
device in the 'Device Feature List'. It's a platform device created by
DFL framework.
This patch adds the basic framework of FME platform driver. It defines
sub feature drivers to handle the different sub features, including init,
uinit and ioctl. It also registers the file operations for the device file.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Kang Luwei <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v3: rename driver from intel-fpga-fme to dfl-fme
rename Kconfig from INTEL_FPGA_FME to FPGA_DFL_FME
---
drivers/fpga/Kconfig | 10 +++
drivers/fpga/Makefile | 3 +
drivers/fpga/dfl-fme-main.c | 160 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 173 insertions(+)
create mode 100644 drivers/fpga/dfl-fme-main.c
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
Userspace application can use these ioctl interfaces to get the API info
and check if specific extension is supported or not in current driver.
This patch implements above 2 ioctls in FPGA Management Engine (FME)
driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: switched to GPLv2 license.
v3: rename intel-fpga.h to fpga-dfl.h and rebased.
---
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/fpga/dfl-fme-main.c | 12 +++++++++
include/uapi/linux/fpga-dfl.h | 50 ++++++++++++++++++++++++++++++++++++
3 files changed, 63 insertions(+)
create mode 100644 include/uapi/linux/fpga-dfl.h
@@ -108,6 +109,13 @@ static void fme_hdr_uinit(struct platform_device *pdev, struct feature *feature)},};+staticlongfme_ioctl_check_extension(structfeature_platform_data*pdata,+unsignedlongarg)+{+/* No extension support for now */+return0;+}+staticintfme_open(structinode*inode,structfile*filp){structplatform_device*fdev=fpga_inode_to_feature_dev(inode);
@@ -148,6 +156,10 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)dev_dbg(&pdev->dev,"%s cmd 0x%x\n",__func__,cmd);switch(cmd){+caseFPGA_GET_API_VERSION:+returnFPGA_API_VERSION;+caseFPGA_CHECK_EXTENSION:+returnfme_ioctl_check_extension(pdata,arg);default:/**Letsub-feature'sioctlfunctiontohandlethecmd
From: Kang Luwei <redacted>
Partial Reconfiguration (PR) is the most important function for FME. It
allows reconfiguration for given Port/Accelerated Function Unit (AFU).
It creates platform devices for fpga-mgr, fpga-regions and fpga-bridges,
and invokes fpga-region's interface (fpga_region_program_fpga) for PR
operation once PR request received via ioctl. Below user space interface
is exposed by this sub feature.
Ioctl interface:
* FPGA_FME_PORT_PR
Do partial reconfiguration per information from userspace, including
target port(AFU), buffer size and address info. It returns error code
to userspace if failed. For detailed PR error information, user needs
to read fpga-mgr's status sysfs interface.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Kang Luwei <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: moved the code to drivers/fpga folder as suggested by Alan Tull.
switched to GPLv2 license.
removed status from FPGA_FME_PORT_PR ioctl data structure.
added platform devices creation for fpga-mgr/fpga-region/fpga-bridge.
switched to fpga-region interface fpga_region_program_fpga for PR.
fixed comments from Alan Tull on FPGA_MGR_PARTIAL_RECONFIG flag usage.
fixed kbuild warnings.
v3: rename driver files to dfl-fme-*.
rebase due to fpga APIs change.
replace bitfields.
switch to fpga_cdev_find_port to find port device.
---
drivers/fpga/Makefile | 2 +-
drivers/fpga/dfl-fme-main.c | 45 +++-
drivers/fpga/dfl-fme-pr.c | 492 ++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/dfl-fme.h | 57 +++++
include/uapi/linux/fpga-dfl.h | 27 +++
5 files changed, 621 insertions(+), 2 deletions(-)
create mode 100644 drivers/fpga/dfl-fme-pr.c
create mode 100644 drivers/fpga/dfl-fme.h
@@ -188,14 +193,49 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg).unlocked_ioctl=fme_ioctl,};+staticintfme_dev_init(structplatform_device*pdev)+{+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+structfpga_fme*fme;++fme=devm_kzalloc(&pdev->dev,sizeof(*fme),GFP_KERNEL);+if(!fme)+return-ENOMEM;++fme->pdata=pdata;++mutex_lock(&pdata->lock);+fpga_pdata_set_private(pdata,fme);+mutex_unlock(&pdata->lock);++return0;+}++staticvoidfme_dev_destroy(structplatform_device*pdev)+{+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+structfpga_fme*fme;++mutex_lock(&pdata->lock);+fme=fpga_pdata_get_private(pdata);+fpga_pdata_set_private(pdata,NULL);+mutex_unlock(&pdata->lock);++devm_kfree(&pdev->dev,fme);+}+staticintfme_probe(structplatform_device*pdev){intret;-ret=fpga_dev_feature_init(pdev,fme_feature_drvs);+ret=fme_dev_init(pdev);if(ret)gotoexit;+ret=fpga_dev_feature_init(pdev,fme_feature_drvs);+if(ret)+gotodev_destroy;+ret=fpga_register_dev_ops(pdev,&fme_fops,THIS_MODULE);if(ret)gotofeature_uinit;
@@ -204,6 +244,8 @@ static int fme_probe(struct platform_device *pdev)feature_uinit:fpga_dev_feature_uinit(pdev);+dev_destroy:+fme_dev_destroy(pdev);exit:returnret;}
@@ -212,6 +254,7 @@ static int fme_remove(struct platform_device *pdev){fpga_dev_feature_uinit(pdev);fpga_unregister_dev_ops(pdev);+fme_dev_destroy(pdev);return0;}
@@ -0,0 +1,492 @@+/*+*DriverforFPGAManagementEngine(FME)PartialReconfiguration+*+*Copyright(C)2017IntelCorporation,Inc.+*+*Authors:+*KangLuwei<luwei.kang@intel.com>+*XiaoGuangrong<guangrong.xiao@linux.intel.com>+*WuHao<hao.wu@intel.com>+*JosephGrecco<joe.grecco@intel.com>+*EnnoLuebbers<enno.luebbers@intel.com>+*TimWhisonant<tim.whisonant@intel.com>+*AnandaRavuri<ananda.ravuri@intel.com>+*ChristopherRauer<christopher.rauer@intel.com>+*HenryMitchel<henry.mitchel@intel.com>+*+*ThisworkislicensedunderthetermsoftheGNUGPLversion2.+*SPDX-License-Identifier:GPL-2.0+*/++#include<linux/types.h>+#include<linux/device.h>+#include<linux/vmalloc.h>+#include<linux/uaccess.h>+#include<linux/fpga/fpga-mgr.h>+#include<linux/fpga/fpga-bridge.h>+#include<linux/fpga/fpga-region.h>+#include<linux/fpga-dfl.h>++#include"fpga-dfl.h"+#include"dfl-fme.h"++staticstructfme_region*+find_fme_region_by_port_id(structfpga_fme*fme,intport_id)+{+structfme_region*fme_region;++list_for_each_entry(fme_region,&fme->region_list,node)+if(fme_region->port_id==port_id)+returnfme_region;++returnNULL;+}++staticintfpga_fme_region_match(structdevice*dev,constvoid*data)+{+returndev->parent==data;+}++staticstructfpga_region*+fpga_fme_region_find(structfpga_fme*fme,intport_id)+{+structfme_region*fme_region;+structfpga_region*region;++fme_region=find_fme_region_by_port_id(fme,port_id);+if(!fme_region)+returnNULL;++region=fpga_region_class_find(NULL,&fme_region->region->dev,+fpga_fme_region_match);+if(!region)+returnNULL;++returnregion;+}++staticintfme_pr(structplatform_device*pdev,unsignedlongarg)+{+void__user*argp=(void__user*)arg;+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+structfpga_fme*fme;+structfpga_image_info*info;+structfpga_region*region;+structfpga_fme_port_prport_pr;+unsignedlongminsz;+void__iomem*fme_hdr;+void*buf=NULL;+intret=0;+u64v;++minsz=offsetofend(structfpga_fme_port_pr,buffer_address);++if(copy_from_user(&port_pr,argp,minsz))+return-EFAULT;++if(port_pr.argsz<minsz||port_pr.flags)+return-EINVAL;++if(!IS_ALIGNED(port_pr.buffer_size,4))+return-EINVAL;++/* get fme header region */+fme_hdr=get_feature_ioaddr_by_index(&pdev->dev,+FME_FEATURE_ID_HEADER);+if(WARN_ON(!fme_hdr))+return-EINVAL;++/* check port id */+v=readq(fme_hdr+FME_HDR_CAP);+if(port_pr.port_id>=FIELD_GET(FME_CAP_NUM_PORTS,v)){+dev_dbg(&pdev->dev,"port number more than maximum\n");+return-EINVAL;+}++if(!access_ok(VERIFY_READ,+(void__user*)(unsignedlong)port_pr.buffer_address,+port_pr.buffer_size))+return-EFAULT;++buf=vmalloc(port_pr.buffer_size);+if(!buf)+return-ENOMEM;++if(copy_from_user(buf,+(void__user*)(unsignedlong)port_pr.buffer_address,+port_pr.buffer_size)){+ret=-EFAULT;+gotofree_exit;+}++/* prepare fpga_image_info for PR */+info=fpga_image_info_alloc(&pdev->dev);+if(!info){+ret=-ENOMEM;+gotofree_exit;+}++info->flags|=FPGA_MGR_PARTIAL_RECONFIG;++mutex_lock(&pdata->lock);+fme=fpga_pdata_get_private(pdata);+/* fme device has been unregistered. */+if(!fme){+ret=-EINVAL;+gotounlock_exit;+}++region=fpga_fme_region_find(fme,port_pr.port_id);+if(!region){+ret=-EINVAL;+gotounlock_exit;+}++fpga_image_info_free(region->info);++info->buf=buf;+info->count=port_pr.buffer_size;+info->region_id=port_pr.port_id;+region->info=info;++ret=fpga_region_program_fpga(region);++if(region->get_bridges)+fpga_bridges_put(®ion->bridge_list);++put_device(®ion->dev);+unlock_exit:+mutex_unlock(&pdata->lock);+free_exit:+vfree(buf);+if(copy_to_user((void__user*)arg,&port_pr,minsz))+return-EFAULT;++returnret;+}++/**+*fpga_fme_create_mgr-createfpgamgrplatformdeviceaschilddevice+*+*@pdata:fmeplatform_device'spdata+*+*Return:mgrplatformdeviceifsuccessful,anderrorcodeotherwise.+*/+staticstructplatform_device*+fpga_fme_create_mgr(structfeature_platform_data*pdata)+{+structplatform_device*mgr,*fme=pdata->dev;+structresourceres;+structresource*pres;+intret=-ENOMEM;++/*+*EachFMEhasonlyonefpga-mgr,soallocateplatformdeviceusing+*thesameFMEplatformdeviceid.+*/+mgr=platform_device_alloc(FPGA_DFL_FME_MGR,fme->id);+if(!mgr)+returnERR_PTR(ret);++mgr->dev.parent=&fme->dev;++pres=platform_get_resource_byname(fme,IORESOURCE_MEM,+FME_FEATURE_PR_MGMT);+if(!pres){+ret=-ENODEV;+gotocreate_mgr_err;+}++memset(&res,0,sizeof(structresource));++res.start=pres->start;+res.end=pres->end;+res.name=pres->name;+res.flags=IORESOURCE_MEM;++ret=platform_device_add_resources(mgr,&res,1);+if(ret)+gotocreate_mgr_err;++ret=platform_device_add(mgr);+if(ret)+gotocreate_mgr_err;++returnmgr;++create_mgr_err:+platform_device_put(mgr);+returnERR_PTR(ret);+}++/**+*fpga_fme_destroy_mgr-destroyfpgamgrplatformdevice+*+*@mgr:fpgamgrplatformdevicetobedestroy+*/+staticvoidfpga_fme_destroy_mgr(structfeature_platform_data*pdata)+{+structfpga_fme*priv=fpga_pdata_get_private(pdata);++platform_device_unregister(priv->mgr);+}++/**+*fpga_fme_create_bridge-createfmefpgabridgeplatformdeviceaschild+*+*@pdata:fmeplatformdevice'spdata+*@port_id:portidforthebridgetobecreated.+*+*Return:bridgeplatformdeviceifsuccessful,anderrorcodeotherwise.+*/+staticstructfme_bridge*+fpga_fme_create_bridge(structfeature_platform_data*pdata,intport_id)+{+structdevice*dev=&pdata->dev->dev;+structfme_br_pdatabr_pdata;+structfme_bridge*fme_br;+intret=-ENOMEM;++fme_br=devm_kzalloc(dev,sizeof(*fme_br),GFP_KERNEL);+if(!fme_br)+returnERR_PTR(ret);++br_pdata.port=fpga_cdev_find_port(fpga_pdata_to_fpga_cdev(pdata),+&port_id,fpga_port_check_id);+if(!br_pdata.port)+returnERR_PTR(-ENODEV);++/*+*EachFPGAdevicemayhavemorethanoneport,soallocateplatform+*deviceusingthesameportplatformdeviceid.+*/+fme_br->br=platform_device_alloc(FPGA_DFL_FME_BRIDGE,+br_pdata.port->id);+if(!fme_br->br){+ret=-ENOMEM;+gotocreate_br_err;+}++fme_br->br->dev.parent=dev;++ret=platform_device_add_data(fme_br->br,&br_pdata,sizeof(br_pdata));+if(ret)+gotocreate_br_err;++ret=platform_device_add(fme_br->br);+if(ret)+gotocreate_br_err;++returnfme_br;++create_br_err:+platform_device_put(fme_br->br);+put_device(&br_pdata.port->dev);+returnERR_PTR(ret);+}++/**+*fpga_fme_destroy_bridge-destroyfpgabridgeplatformdevice+*@br:fpgabridgeplatformdevicetobedestroy+*/+staticvoidfpga_fme_destroy_bridge(structfeature_platform_data*pdata,+structfme_bridge*fme_br)+{+structfme_br_pdata*br_pdata=dev_get_platdata(&fme_br->br->dev);++put_device(&br_pdata->port->dev);+platform_device_unregister(fme_br->br);+}++staticvoidfpga_fme_destroy_bridges(structfeature_platform_data*pdata)+{+structfpga_fme*priv=fpga_pdata_get_private(pdata);+structfme_bridge*fbridge,*tmp;++list_for_each_entry_safe(fbridge,tmp,&priv->bridge_list,node){+list_del(&fbridge->node);+fpga_fme_destroy_bridge(pdata,fbridge);+}+}++/**+*fpga_fme_create_region-createfpgaregionplatformdeviceaschild+*+*@pdata:fmeplatformdevice'spdata+*@mgr:mgrplatformdeviceneededforregion+*@br:brplatformdeviceneededforregion+*@port_id:portid+*+*Return:fmeregionifsuccessful,anderrorcodeotherwise.+*/+staticstructfme_region*+fpga_fme_create_region(structfeature_platform_data*pdata,+structplatform_device*mgr,+structplatform_device*br,intport_id)+{+structdevice*dev=&pdata->dev->dev;+structfme_region_pdataregion_pdata;+structfme_region*fme_region;+intret=-ENOMEM;++fme_region=devm_kzalloc(dev,sizeof(*fme_region),GFP_KERNEL);+if(!fme_region)+returnERR_PTR(ret);++region_pdata.mgr=mgr;+region_pdata.br=br;++/*+*EachFPGAdevicemayhavemorethanoneport,soallocateplatform+*deviceusingthesameportplatformdeviceid.+*/+fme_region->region=platform_device_alloc(FPGA_DFL_FME_REGION,br->id);+if(!fme_region->region)+returnERR_PTR(ret);++fme_region->region->dev.parent=dev;++ret=platform_device_add_data(fme_region->region,®ion_pdata,+sizeof(region_pdata));+if(ret)+gotocreate_region_err;++ret=platform_device_add(fme_region->region);+if(ret)+gotocreate_region_err;++fme_region->port_id=port_id;++returnfme_region;++create_region_err:+platform_device_put(fme_region->region);+returnERR_PTR(ret);+}++/**+*fpga_fme_destroy_region-destroyfmeregion+*@region:fmeregiontobedestroy+*/+staticvoidfpga_fme_destroy_region(structfeature_platform_data*pdata,+structfme_region*fme_region)+{+platform_device_unregister(fme_region->region);+}++staticvoidfpga_fme_destroy_regions(structfeature_platform_data*pdata)+{+structfpga_fme*priv=fpga_pdata_get_private(pdata);+structfme_region*fme_region,*tmp;++list_for_each_entry_safe(fme_region,tmp,&priv->region_list,node){+list_del(&fme_region->node);+fpga_fme_destroy_region(pdata,fme_region);+}+}++staticintpr_mgmt_init(structplatform_device*pdev,structfeature*feature)+{+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+void__iomem*fme_hdr;+structplatform_device*mgr;+structfme_region*fme_region;+structfme_bridge*fme_br;+structfpga_fme*priv;+intret=-ENODEV,i=0;+u64fme_cap,port_offset;++fme_hdr=get_feature_ioaddr_by_index(&pdev->dev,+FME_FEATURE_ID_HEADER);+if(WARN_ON(!fme_hdr))+return-EINVAL;++mutex_lock(&pdata->lock);+priv=fpga_pdata_get_private(pdata);++/* Initialize the region and bridge sub device list */+INIT_LIST_HEAD(&priv->region_list);+INIT_LIST_HEAD(&priv->bridge_list);++/* Create fpga mgr platform device */+mgr=fpga_fme_create_mgr(pdata);+if(IS_ERR(mgr)){+dev_err(&pdev->dev,"fail to create fpga mgr pdev\n");+gotounlock;+}++priv->mgr=mgr;++/* Read capability register to check number of regions and bridges */+fme_cap=readq(fme_hdr+FME_HDR_CAP);+for(;i<FIELD_GET(FME_CAP_NUM_PORTS,fme_cap);i++){+port_offset=readq(fme_hdr+FME_HDR_PORT_OFST(i));+if(!(port_offset&FME_PORT_OFST_IMP))+continue;++/* Create bridge for each port */+fme_br=fpga_fme_create_bridge(pdata,i);+if(IS_ERR(fme_br)){+ret=PTR_ERR(fme_br);+gotodestroy_region;+}++list_add(&fme_br->node,&priv->bridge_list);++/* Create region for each port */+fme_region=fpga_fme_create_region(pdata,mgr,fme_br->br,i);+if(!fme_region){+ret=PTR_ERR(fme_region);+gotodestroy_region;+}++list_add(&fme_region->node,&priv->region_list);+}+mutex_unlock(&pdata->lock);++return0;++destroy_region:+fpga_fme_destroy_regions(pdata);+fpga_fme_destroy_bridges(pdata);+fpga_fme_destroy_mgr(pdata);+unlock:+mutex_unlock(&pdata->lock);+returnret;+}++staticvoidpr_mgmt_uinit(structplatform_device*pdev,structfeature*feature)+{+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+structfpga_fme*priv;++mutex_lock(&pdata->lock);+priv=fpga_pdata_get_private(pdata);++fpga_fme_destroy_regions(pdata);+fpga_fme_destroy_bridges(pdata);+fpga_fme_destroy_mgr(pdata);+mutex_unlock(&pdata->lock);+}++staticlongfme_pr_ioctl(structplatform_device*pdev,structfeature*feature,+unsignedintcmd,unsignedlongarg)+{+longret;++switch(cmd){+caseFPGA_FME_PORT_PR:+ret=fme_pr(pdev,arg);+break;+default:+ret=-ENODEV;+}++returnret;+}++conststructfeature_opspr_mgmt_ops={+.init=pr_mgmt_init,+.uinit=pr_mgmt_uinit,+.ioctl=fme_pr_ioctl,+};
@@ -0,0 +1,8 @@+What: /sys/bus/platform/devices/fpga-dfl-fme-mgr.0/interface_id+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read-only. It returns interface id of partial reconfiguration+ hardware. Userspace could use this information to check if+ current hardware is compatible with given image before FPGA+ programming.
@@ -0,0 +1,318 @@+/*+*FPGAManagerDriverforFPGAManagementEngine(FME)+*+*Copyright(C)2017IntelCorporation,Inc.+*+*Authors:+*KangLuwei<luwei.kang@intel.com>+*XiaoGuangrong<guangrong.xiao@linux.intel.com>+*WuHao<hao.wu@intel.com>+*JosephGrecco<joe.grecco@intel.com>+*EnnoLuebbers<enno.luebbers@intel.com>+*TimWhisonant<tim.whisonant@intel.com>+*AnandaRavuri<ananda.ravuri@intel.com>+*ChristopherRauer<christopher.rauer@intel.com>+*HenryMitchel<henry.mitchel@intel.com>+*+*ThisworkislicensedunderthetermsoftheGNUGPLversion2.+*SPDX-License-Identifier:GPL-2.0+*/++#include<linux/module.h>+#include<linux/iopoll.h>+#include<linux/fpga/fpga-mgr.h>++#include"fpga-dfl.h"+#include"dfl-fme.h"++#define PR_WAIT_TIMEOUT 8000000+#define PR_HOST_STATUS_IDLE 0++structfme_mgr_priv{+void__iomem*ioaddr;+u64pr_error;+};++staticssize_tinterface_id_show(structdevice*dev,+structdevice_attribute*attr,char*buf)+{+structfpga_manager*mgr=dev_get_drvdata(dev);+structfme_mgr_priv*priv=mgr->priv;+u64intfc_id_l,intfc_id_h;++intfc_id_l=readq(priv->ioaddr+FME_PR_INTFC_ID_L);+intfc_id_h=readq(priv->ioaddr+FME_PR_INTFC_ID_H);++returnscnprintf(buf,PAGE_SIZE,"%016llx%016llx\n",+(unsignedlonglong)intfc_id_h,+(unsignedlonglong)intfc_id_l);+}+staticDEVICE_ATTR_RO(interface_id);++staticconststructattribute*fme_mgr_attrs[]={+&dev_attr_interface_id.attr,+NULL,+};++staticu64pr_error_to_mgr_status(u64err)+{+u64status=0;++if(err&FME_PR_ERR_OPERATION_ERR)+status|=FPGA_MGR_STATUS_OPERATION_ERR;+if(err&FME_PR_ERR_CRC_ERR)+status|=FPGA_MGR_STATUS_CRC_ERR;+if(err&FME_PR_ERR_INCOMPATIBLE_BS)+status|=FPGA_MGR_STATUS_INCOMPATIBLE_IMAGE_ERR;+if(err&FME_PR_ERR_PROTOCOL_ERR)+status|=FPGA_MGR_STATUS_IP_PROTOCOL_ERR;+if(err&FME_PR_ERR_FIFO_OVERFLOW)+status|=FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR;++returnstatus;+}++staticu64fme_mgr_pr_error_handle(void__iomem*fme_pr)+{+u64pr_status,pr_error;++pr_status=readq(fme_pr+FME_PR_STS);+if(!(pr_status&FME_PR_STS_PR_STS))+return0;++pr_error=readq(fme_pr+FME_PR_ERR);+writeq(pr_error,fme_pr+FME_PR_ERR);++returnpr_error;+}++staticintfme_mgr_write_init(structfpga_manager*mgr,+structfpga_image_info*info,+constchar*buf,size_tcount)+{+structdevice*dev=&mgr->dev;+structfme_mgr_priv*priv=mgr->priv;+void__iomem*fme_pr=priv->ioaddr;+u64pr_ctrl,pr_status;++if(!(info->flags&FPGA_MGR_PARTIAL_RECONFIG)){+dev_err(dev,"only support partial reconfiguration.\n");+return-EINVAL;+}++dev_dbg(dev,"resetting PR before initiated PR\n");++pr_ctrl=readq(fme_pr+FME_PR_CTRL);+pr_ctrl|=FME_PR_CTRL_PR_RST;+writeq(pr_ctrl,fme_pr+FME_PR_CTRL);++if(readq_poll_timeout(fme_pr+FME_PR_CTRL,pr_ctrl,+pr_ctrl&FME_PR_CTRL_PR_RSTACK,1,+PR_WAIT_TIMEOUT)){+dev_err(dev,"maximum PR timeout\n");+return-ETIMEDOUT;+}++pr_ctrl=readq(fme_pr+FME_PR_CTRL);+pr_ctrl&=~FME_PR_CTRL_PR_RST;+writeq(pr_ctrl,fme_pr+FME_PR_CTRL);++dev_dbg(dev,+"waiting for PR resource in HW to be initialized and ready\n");++if(readq_poll_timeout(fme_pr+FME_PR_STS,pr_status,+(pr_status&FME_PR_STS_PR_STS)==+FME_PR_STS_PR_STS_IDLE,1,PR_WAIT_TIMEOUT)){+dev_err(dev,"maximum PR timeout\n");+priv->pr_error=fme_mgr_pr_error_handle(fme_pr);+return-ETIMEDOUT;+}++dev_dbg(dev,"check and clear previous PR error\n");+priv->pr_error=fme_mgr_pr_error_handle(fme_pr);+if(priv->pr_error)+dev_dbg(dev,"previous PR error detected %llx\n",+(unsignedlonglong)priv->pr_error);++dev_dbg(dev,"set PR port ID\n");++pr_ctrl=readq(fme_pr+FME_PR_CTRL);+pr_ctrl&=~FME_PR_CTRL_PR_RGN_ID;+pr_ctrl|=FIELD_PREP(FME_PR_CTRL_PR_RGN_ID,info->region_id);+writeq(pr_ctrl,fme_pr+FME_PR_CTRL);++return0;+}++staticintfme_mgr_write(structfpga_manager*mgr,+constchar*buf,size_tcount)+{+structdevice*dev=&mgr->dev;+structfme_mgr_priv*priv=mgr->priv;+void__iomem*fme_pr=priv->ioaddr;+u64pr_ctrl,pr_status,pr_data;+intdelay=0,pr_credit,i=0;++dev_dbg(dev,"start request\n");++pr_ctrl=readq(fme_pr+FME_PR_CTRL);+pr_ctrl|=FME_PR_CTRL_PR_START;+writeq(pr_ctrl,fme_pr+FME_PR_CTRL);++dev_dbg(dev,"pushing data from bitstream to HW\n");++pr_status=readq(fme_pr+FME_PR_STS);+pr_credit=FIELD_GET(FME_PR_STS_PR_CREDIT,pr_status);++while(count>0){+while(pr_credit<=1){+if(delay++>PR_WAIT_TIMEOUT){+dev_err(dev,"maximum try\n");+return-ETIMEDOUT;+}+udelay(1);++pr_status=readq(fme_pr+FME_PR_STS);+pr_credit=FIELD_GET(FME_PR_STS_PR_CREDIT,pr_status);+}++if(count>=4){+pr_data=0;+pr_data|=FIELD_PREP(FME_PR_DATA_PR_DATA_RAW,+*(((u32*)buf)+i));+writeq(pr_data,fme_pr+FME_PR_DATA);+count-=4;+pr_credit--;+i++;+}else{+WARN_ON(1);+return-EINVAL;+}+}++return0;+}++staticintfme_mgr_write_complete(structfpga_manager*mgr,+structfpga_image_info*info)+{+structdevice*dev=&mgr->dev;+structfme_mgr_priv*priv=mgr->priv;+void__iomem*fme_pr=priv->ioaddr;+u64pr_ctrl;++pr_ctrl=readq(fme_pr+FME_PR_CTRL);+pr_ctrl|=FME_PR_CTRL_PR_COMPLETE;+writeq(pr_ctrl,fme_pr+FME_PR_CTRL);++dev_dbg(dev,"green bitstream push complete\n");+dev_dbg(dev,"waiting for HW to release PR resource\n");++if(readq_poll_timeout(fme_pr+FME_PR_CTRL,pr_ctrl,+!(pr_ctrl&FME_PR_CTRL_PR_START),1,+PR_WAIT_TIMEOUT)){+dev_err(dev,"maximum try.\n");+return-ETIMEDOUT;+}++dev_dbg(dev,"PR operation complete, checking status\n");+priv->pr_error=fme_mgr_pr_error_handle(fme_pr);+if(priv->pr_error){+dev_dbg(dev,"PR error detected %llx\n",+(unsignedlonglong)priv->pr_error);+return-EIO;+}++dev_dbg(dev,"PR done successfully\n");++return0;+}++staticenumfpga_mgr_statesfme_mgr_state(structfpga_manager*mgr)+{+returnFPGA_MGR_STATE_UNKNOWN;+}++staticu64fme_mgr_status(structfpga_manager*mgr)+{+structfme_mgr_priv*priv=mgr->priv;++returnpr_error_to_mgr_status(priv->pr_error);+}++staticconststructfpga_manager_opsfme_mgr_ops={+.write_init=fme_mgr_write_init,+.write=fme_mgr_write,+.write_complete=fme_mgr_write_complete,+.state=fme_mgr_state,+.status=fme_mgr_status,+};++staticintfme_mgr_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structfme_mgr_priv*priv;+structfpga_manager*mgr;+structresource*res;+intret;++priv=devm_kzalloc(dev,sizeof(*priv),GFP_KERNEL);+if(!priv)+return-ENOMEM;++res=platform_get_resource(pdev,IORESOURCE_MEM,0);+priv->ioaddr=devm_ioremap(dev,res->start,resource_size(res));+if(IS_ERR(priv->ioaddr))+returnPTR_ERR(priv->ioaddr);++ret=sysfs_create_files(&pdev->dev.kobj,fme_mgr_attrs);+if(ret)+returnret;++mgr=devm_kzalloc(dev,sizeof(*mgr),GFP_KERNEL);+if(!mgr)+gotosysfs_remove_exit;++mgr->name="DFL FPGA Manager";+mgr->mops=&fme_mgr_ops;+mgr->priv=priv;+mgr->parent=dev;+platform_set_drvdata(pdev,mgr);++ret=fpga_mgr_register(mgr);+if(ret){+dev_err(dev,"unable to register FPGA manager\n");+gotosysfs_remove_exit;+}++return0;++sysfs_remove_exit:+sysfs_remove_files(&pdev->dev.kobj,fme_mgr_attrs);+returnret;+}++staticintfme_mgr_remove(structplatform_device*pdev)+{+structfpga_manager*mgr=platform_get_drvdata(pdev);++fpga_mgr_unregister(mgr);+sysfs_remove_files(&pdev->dev.kobj,fme_mgr_attrs);++return0;+}++staticstructplatform_driverfme_mgr_driver={+.driver={+.name=FPGA_DFL_FME_MGR,+},+.probe=fme_mgr_probe,+.remove=fme_mgr_remove,+};++module_platform_driver(fme_mgr_driver);++MODULE_DESCRIPTION("FPGA Manager for FPGA Management Engine");+MODULE_AUTHOR("Intel Corporation");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:fpga-dfl-fme-mgr");
This patch adds fpga region platform driver for FPGA Management Engine.
It register an fpga region with given fpga manager / bridge device.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Wu Hao <redacted>
----
v3: rename driver to fpga-dfl-fme-region
fix fpga_mgr_put order problem in remove function.
rebased due to fpga api changes.
---
drivers/fpga/Kconfig | 6 +++
drivers/fpga/Makefile | 1 +
drivers/fpga/fpga-dfl-fme-region.c | 92 ++++++++++++++++++++++++++++++++++++++
3 files changed, 99 insertions(+)
create mode 100644 drivers/fpga/fpga-dfl-fme-region.c
@@ -0,0 +1,92 @@+/*+*FPGARegionDriverforFPGAManagementEngine(FME)+*+*Copyright(C)2017IntelCorporation,Inc.+*+*Authors:+*WuHao<hao.wu@intel.com>+*JosephGrecco<joe.grecco@intel.com>+*EnnoLuebbers<enno.luebbers@intel.com>+*TimWhisonant<tim.whisonant@intel.com>+*AnandaRavuri<ananda.ravuri@intel.com>+*HenryMitchel<henry.mitchel@intel.com>+*+*ThisworkislicensedunderthetermsoftheGNUGPLversion2.+*SPDX-License-Identifier:GPL-2.0+*/++#include<linux/module.h>+#include<linux/fpga/fpga-region.h>++#include"fpga-dfl.h"+#include"dfl-fme.h"++staticintfme_region_get_bridges(structfpga_region*region)+{+structfme_region_pdata*pdata=region->priv;+structdevice*dev=&pdata->br->dev;++returnfpga_bridge_get_to_list(dev,region->info,®ion->bridge_list);+}++staticintfme_region_probe(structplatform_device*pdev)+{+structdevice*dev=&pdev->dev;+structfme_region_pdata*pdata=dev_get_platdata(dev);+structfpga_region*region;+structfpga_manager*mgr;+intret;++mgr=fpga_mgr_get(&pdata->mgr->dev);+if(IS_ERR(mgr))+return-EPROBE_DEFER;++region=devm_kzalloc(dev,sizeof(*region),GFP_KERNEL);+if(!region){+ret=-ENOMEM;+gotoeprobe_mgr_put;+}++region->mgr=mgr;+region->get_bridges=fme_region_get_bridges;+region->priv=pdata;+region->parent=dev;+platform_set_drvdata(pdev,region);++ret=fpga_region_register(region);+if(ret)+gotoeprobe_mgr_put;++dev_dbg(dev,"DFL FME FPGA Region probed\n");++return0;++eprobe_mgr_put:+fpga_mgr_put(mgr);+returnret;+}++staticintfme_region_remove(structplatform_device*pdev)+{+structfpga_region*region=dev_get_drvdata(&pdev->dev);++fpga_region_unregister(region);+fpga_mgr_put(region->mgr);++return0;+}++staticstructplatform_driverfme_region_driver={+.driver={+.name=FPGA_DFL_FME_REGION,+},+.probe=fme_region_probe,+.remove=fme_region_remove,+};++module_platform_driver(fme_region_driver);++MODULE_DESCRIPTION("FPGA Region for FPGA Management Engine");+MODULE_AUTHOR("Intel Corporation");+MODULE_LICENSE("GPL v2");+MODULE_ALIAS("platform:fpga-dfl-fme-region");
On DFL FPGA devices, the Accelerated Function Unit (AFU), can be
reprogrammed for different functions. It connects to the FPGA
infrastructure("blue bistream") via a Port. Port CSRs are implemented
separately from the AFU CSRs to provide control and status of the Port.
Once valid green bitstream is programmed into the AFU, it allows access
to the AFU CSRs in the AFU MMIO space.
This patch only implements basic driver framework for AFU, including
device file operation framework.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v3: rename driver to dfl-afu-main
---
drivers/fpga/Kconfig | 9 +++
drivers/fpga/Makefile | 2 +
drivers/fpga/dfl-afu-main.c | 161 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 172 insertions(+)
create mode 100644 drivers/fpga/dfl-afu-main.c
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
This patch implements above 2 ioctls in FPGA Accelerated Function Unit
(AFU) driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: rebased
v3: rebased as driver renamed to fpga-dfl-afu
fix one checkpatch issue
---
drivers/fpga/dfl-afu-main.c | 11 +++++++++++
1 file changed, 11 insertions(+)
@@ -123,6 +123,13 @@ static int afu_release(struct inode *inode, struct file *filp)return0;}+staticlongafu_ioctl_check_extension(structfeature_platform_data*pdata,+unsignedlongarg)+{+/* No extension support for now */+return0;+}+staticlongafu_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){structplatform_device*pdev=filp->private_data;
@@ -133,6 +140,10 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)dev_dbg(&pdev->dev,"%s cmd 0x%x\n",__func__,cmd);switch(cmd){+caseFPGA_GET_API_VERSION:+returnFPGA_API_VERSION;+caseFPGA_CHECK_EXTENSION:+returnafu_ioctl_check_extension(pdata,arg);default:/**Letsub-feature'sioctlfunctiontohandlethecmd
DMA memory regions are required for Accelerated Function Unit (AFU) usage.
These two ioctls allow user space applications to map user memory regions
for dma, and unmap them after use. Iova is returned from driver to user
space application via FPGA_PORT_DMA_MAP ioctl. Application needs to unmap
it after use, otherwise, driver will unmap them in device file release
operation.
Each AFU has its own rb tree to keep track of its mapped DMA regions.
Ioctl interfaces:
* FPGA_PORT_DMA_MAP
Do the dma mapping per user_addr and length which provided by user.
Return iova in provided struct afu_port_dma_map.
* FPGA_PORT_DMA_UNMAP
Unmap the dma region per iova provided by user.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
-----
v2: moved the code to drivers/fpga folder as suggested by Alan Tull.
switched to GPLv2 license.
fixed kbuild warnings.
v3: improved commit description and fixed coding style issues.
replaced fpga_pdata_to_pcidev with fpga_pdata_to_fpga_cdev
---
drivers/fpga/Makefile | 2 +-
drivers/fpga/dfl-afu-dma-region.c | 465 ++++++++++++++++++++++++++++++++++++++
drivers/fpga/dfl-afu-main.c | 61 ++++-
drivers/fpga/dfl-afu.h | 18 ++
include/uapi/linux/fpga-dfl.h | 37 +++
5 files changed, 581 insertions(+), 2 deletions(-)
create mode 100644 drivers/fpga/dfl-afu-dma-region.c
@@ -0,0 +1,465 @@+/*+*DriverforFPGAAcceleratedFunctionUnit(AFU)DMARegionManagement+*+*Copyright(C)2017IntelCorporation,Inc.+*+*Authors:+*WuHao<hao.wu@intel.com>+*XiaoGuangrong<guangrong.xiao@linux.intel.com>+*+*ThisworkislicensedunderthetermsoftheGNUGPLversion2.+*SPDX-License-Identifier:GPL-2.0+*/++#include<linux/dma-mapping.h>+#include<linux/sched/signal.h>+#include<linux/uaccess.h>++#include"dfl-afu.h"++staticvoidput_all_pages(structpage**pages,intnpages)+{+inti;++for(i=0;i<npages;i++)+if(pages[i])+put_page(pages[i]);+}++voidafu_dma_region_init(structfeature_platform_data*pdata)+{+structfpga_afu*afu=fpga_pdata_get_private(pdata);++afu->dma_regions=RB_ROOT;+}++/**+*afu_dma_adjust_locked_vm-adjustlockedmemory+*@dev:portdevice+*@npages:numberofpages+*@incr:increaseordecreaselockedmemory+*+*Increaseordecreasethelockedmemorysizewithnpagesinput.+*+*Return0onsuccess.+*Return-ENOMEMiflockedmemorysizeisoverthelimitandnoCAP_IPC_LOCK.+*/+staticintafu_dma_adjust_locked_vm(structdevice*dev,longnpages,boolincr)+{+unsignedlonglocked,lock_limit;+intret=0;++/* the task is exiting. */+if(!current->mm)+return0;++down_write(¤t->mm->mmap_sem);++if(incr){+locked=current->mm->locked_vm+npages;+lock_limit=rlimit(RLIMIT_MEMLOCK)>>PAGE_SHIFT;++if(locked>lock_limit&&!capable(CAP_IPC_LOCK))+ret=-ENOMEM;+else+current->mm->locked_vm+=npages;+}else{+if(WARN_ON_ONCE(npages>current->mm->locked_vm))+npages=current->mm->locked_vm;+current->mm->locked_vm-=npages;+}++dev_dbg(dev,"[%d] RLIMIT_MEMLOCK %c%ld %ld/%ld%s\n",current->pid,+incr?'+':'-',npages<<PAGE_SHIFT,+current->mm->locked_vm<<PAGE_SHIFT,rlimit(RLIMIT_MEMLOCK),+ret?"- execeeded":"");++up_write(¤t->mm->mmap_sem);++returnret;+}++/**+*afu_dma_pin_pages-pinpagesofgivendmamemoryregion+*@pdata:featuredeviceplatformdata+*@region:dmamemoryregiontobepinned+*+*Pinallthepagesofgivenfpga_afu_dma_region.+*Return0forsuccessornegativeerrorcode.+*/+staticintafu_dma_pin_pages(structfeature_platform_data*pdata,+structfpga_afu_dma_region*region)+{+intnpages=region->length>>PAGE_SHIFT;+structdevice*dev=&pdata->dev->dev;+intret,pinned;++ret=afu_dma_adjust_locked_vm(dev,npages,true);+if(ret)+returnret;++region->pages=kcalloc(npages,sizeof(structpage*),GFP_KERNEL);+if(!region->pages){+ret=-ENOMEM;+gotounlock_vm;+}++pinned=get_user_pages_fast(region->user_addr,npages,1,+region->pages);+if(pinned<0){+ret=pinned;+gotoput_pages;+}elseif(pinned!=npages){+ret=-EFAULT;+gotofree_pages;+}++dev_dbg(dev,"%d pages pinned\n",pinned);++return0;++put_pages:+put_all_pages(region->pages,pinned);+free_pages:+kfree(region->pages);+unlock_vm:+afu_dma_adjust_locked_vm(dev,npages,false);+returnret;+}++/**+*afu_dma_unpin_pages-unpinpagesofgivendmamemoryregion+*@pdata:featuredeviceplatformdata+*@region:dmamemoryregiontobeunpinned+*+*Unpinallthepagesofgivenfpga_afu_dma_region.+*Return0forsuccessornegativeerrorcode.+*/+staticvoidafu_dma_unpin_pages(structfeature_platform_data*pdata,+structfpga_afu_dma_region*region)+{+longnpages=region->length>>PAGE_SHIFT;+structdevice*dev=&pdata->dev->dev;++put_all_pages(region->pages,npages);+kfree(region->pages);+afu_dma_adjust_locked_vm(dev,npages,false);++dev_dbg(dev,"%ld pages unpinned\n",npages);+}++/**+*afu_dma_check_continuous_pages-checkifpagesarecontinuous+*@region:dmamemoryregion+*+*Returntrueifpagesofgivendmamemoryregionhavecontinuousphysical+*address,otherwisereturnfalse.+*/+staticboolafu_dma_check_continuous_pages(structfpga_afu_dma_region*region)+{+intnpages=region->length>>PAGE_SHIFT;+inti;++for(i=0;i<npages-1;i++)+if(page_to_pfn(region->pages[i])+1!=+page_to_pfn(region->pages[i+1]))+returnfalse;++returntrue;+}++/**+*dma_region_check_iova-checkifmemoryareaisfullycontainedintheregion+*@region:dmamemoryregion+*@iova:addressofthedmamemoryarea+*@size:sizeofthedmamemoryarea+*+*Comparethedmamemoryareadefinedby@iovaand@sizewithgivendmaregion.+*Returntrueifmemoryareaisfullycontainedintheregion,otherwisefalse.+*/+staticbooldma_region_check_iova(structfpga_afu_dma_region*region,+u64iova,u64size)+{+if(!size&®ion->iova!=iova)+returnfalse;++return(region->iova<=iova)&&+(region->length+region->iova>=iova+size);+}++/**+*afu_dma_region_add-addgivendmaregiontorbtree+*@pdata:featuredeviceplatformdata+*@region:dmaregiontobeadded+*+*Return0forsuccess,-EEXISTifdmaregionhasalreadybeenadded.+*+*Needstobecalledwithpdata->lockheold.+*/+staticintafu_dma_region_add(structfeature_platform_data*pdata,+structfpga_afu_dma_region*region)+{+structfpga_afu*afu=fpga_pdata_get_private(pdata);+structrb_node**new,*parent=NULL;++dev_dbg(&pdata->dev->dev,"add region (iova = %llx)\n",+(unsignedlonglong)region->iova);++new=&afu->dma_regions.rb_node;++while(*new){+structfpga_afu_dma_region*this;++this=container_of(*new,structfpga_afu_dma_region,node);++parent=*new;++if(dma_region_check_iova(this,region->iova,region->length))+return-EEXIST;++if(region->iova<this->iova)+new=&((*new)->rb_left);+elseif(region->iova>this->iova)+new=&((*new)->rb_right);+else+return-EEXIST;+}++rb_link_node(®ion->node,parent,new);+rb_insert_color(®ion->node,&afu->dma_regions);++return0;+}++/**+*afu_dma_region_remove-removegivendmaregionfromrbtree+*@pdata:featuredeviceplatformdata+*@region:dmaregiontoberemoved+*+*Needstobecalledwithpdata->lockheold.+*/+staticvoidafu_dma_region_remove(structfeature_platform_data*pdata,+structfpga_afu_dma_region*region)+{+structfpga_afu*afu;++dev_dbg(&pdata->dev->dev,"del region (iova = %llx)\n",+(unsignedlonglong)region->iova);++afu=fpga_pdata_get_private(pdata);+rb_erase(®ion->node,&afu->dma_regions);+}++/**+*afu_dma_region_destroy-destroyallregionsinrbtree+*@pdata:featuredeviceplatformdata+*+*Needstobecalledwithpdata->lockheold.+*/+voidafu_dma_region_destroy(structfeature_platform_data*pdata)+{+structfpga_afu*afu=fpga_pdata_get_private(pdata);+structrb_node*node=rb_first(&afu->dma_regions);+structfpga_afu_dma_region*region;++while(node){+region=container_of(node,structfpga_afu_dma_region,node);++dev_dbg(&pdata->dev->dev,"del region (iova = %llx)\n",+(unsignedlonglong)region->iova);++rb_erase(node,&afu->dma_regions);++if(region->iova)+dma_unmap_page(fpga_pdata_to_parent(pdata),+region->iova,region->length,+DMA_BIDIRECTIONAL);++if(region->pages)+afu_dma_unpin_pages(pdata,region);++node=rb_next(node);+kfree(region);+}+}++/**+*afu_dma_region_find-findthedmaregionfromrbtreebasedoniovaandsize+*@pdata:featuredeviceplatformdata+*@iova:addressofthedmamemoryarea+*@size:sizeofthedmamemoryarea+*+*Itfindsthedmaregionfromtherbtreebasedon@iovaand@size:+*-if@size==0,itfindsthedmaregionwhichstartsfrom@iova+*-otherwise,itfindsthedmaregionwhichfullycontains+*[@iova,@iova+size)+*IfnothingismatchedreturnsNULL.+*+*Needstobecalledwithpdata->lockheld.+*/+structfpga_afu_dma_region*+afu_dma_region_find(structfeature_platform_data*pdata,u64iova,u64size)+{+structfpga_afu*afu=fpga_pdata_get_private(pdata);+structrb_node*node=afu->dma_regions.rb_node;+structdevice*dev=&pdata->dev->dev;++while(node){+structfpga_afu_dma_region*region;++region=container_of(node,structfpga_afu_dma_region,node);++if(dma_region_check_iova(region,iova,size)){+dev_dbg(dev,"find region (iova = %llx)\n",+(unsignedlonglong)region->iova);+returnregion;+}++if(iova<region->iova)+node=node->rb_left;+elseif(iova>region->iova)+node=node->rb_right;+else+/* the iova region is not fully covered. */+break;+}++dev_dbg(dev,"region with iova %llx and size %llx is not found\n",+(unsignedlonglong)iova,(unsignedlonglong)size);++returnNULL;+}++/**+*afu_dma_region_find_iova-findthedmaregionfromrbtreebyiova+*@pdata:featuredeviceplatformdata+*@iova:addressofthedmaregion+*+*Needstobecalledwithpdata->lockheld.+*/+staticstructfpga_afu_dma_region*+afu_dma_region_find_iova(structfeature_platform_data*pdata,u64iova)+{+returnafu_dma_region_find(pdata,iova,0);+}++/**+*afu_dma_map_region-mapmemoryregionfordma+*@pdata:featuredeviceplatformdata+*@user_addr:addressofthememoryregion+*@length:sizeofthememoryregion+*@iova:pointerofiovaaddress+*+*Mapmemoryregiondefinedby@user_addrand@length,andreturndmaaddress+*ofthememoryregionvia@iova.+*Return0forsuccess,otherwiseerrorcode.+*/+intafu_dma_map_region(structfeature_platform_data*pdata,+u64user_addr,u64length,u64*iova)+{+structfpga_afu_dma_region*region;+intret;++/*+*CheckInputs,onlyacceptpage-alignedusermemoryregionwith+*validlength.+*/+if(!PAGE_ALIGNED(user_addr)||!PAGE_ALIGNED(length)||!length)+return-EINVAL;++/* Check overflow */+if(user_addr+length<user_addr)+return-EINVAL;++if(!access_ok(VERIFY_WRITE,(void__user*)(unsignedlong)user_addr,+length))+return-EINVAL;++region=kzalloc(sizeof(*region),GFP_KERNEL);+if(!region)+return-ENOMEM;++region->user_addr=user_addr;+region->length=length;++/* Pin the user memory region */+ret=afu_dma_pin_pages(pdata,region);+if(ret){+dev_err(&pdata->dev->dev,"failed to pin memory region\n");+gotofree_region;+}++/* Only accept continuous pages, return error else */+if(!afu_dma_check_continuous_pages(region)){+dev_err(&pdata->dev->dev,"pages are not continuous\n");+ret=-EINVAL;+gotounpin_pages;+}++/* As pages are continuous then start to do DMA mapping */+region->iova=dma_map_page(fpga_pdata_to_parent(pdata),+region->pages[0],0,+region->length,+DMA_BIDIRECTIONAL);+if(dma_mapping_error(&pdata->dev->dev,region->iova)){+dev_err(&pdata->dev->dev,"failed to map for dma\n");+ret=-EFAULT;+gotounpin_pages;+}++*iova=region->iova;++mutex_lock(&pdata->lock);+ret=afu_dma_region_add(pdata,region);+mutex_unlock(&pdata->lock);+if(ret){+dev_err(&pdata->dev->dev,"failed to add dma region\n");+gotounmap_dma;+}++return0;++unmap_dma:+dma_unmap_page(fpga_pdata_to_parent(pdata),+region->iova,region->length,DMA_BIDIRECTIONAL);+unpin_pages:+afu_dma_unpin_pages(pdata,region);+free_region:+kfree(region);+returnret;+}++/**+*afu_dma_unmap_region-unmapdmamemoryregion+*@pdata:featuredeviceplatformdata+*@iova:dmaaddressoftheregion+*+*Unmapdmamemoryregionbasedon@iova.+*Return0forsuccess,otherwiseerrorcode.+*/+intafu_dma_unmap_region(structfeature_platform_data*pdata,u64iova)+{+structfpga_afu_dma_region*region;++mutex_lock(&pdata->lock);+region=afu_dma_region_find_iova(pdata,iova);+if(!region){+mutex_unlock(&pdata->lock);+return-EINVAL;+}++if(region->in_use){+mutex_unlock(&pdata->lock);+return-EBUSY;+}++afu_dma_region_remove(pdata,region);+mutex_unlock(&pdata->lock);++dma_unmap_page(fpga_pdata_to_parent(pdata),+region->iova,region->length,DMA_BIDIRECTIONAL);+afu_dma_unpin_pages(pdata,region);+kfree(region);++return0;+}
@@ -269,6 +322,10 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)returnafu_ioctl_get_info(pdata,(void__user*)arg);caseFPGA_PORT_GET_REGION_INFO:returnafu_ioctl_get_region_info(pdata,(void__user*)arg);+caseFPGA_PORT_DMA_MAP:+returnafu_ioctl_dma_map(pdata,(void__user*)arg);+caseFPGA_PORT_DMA_UNMAP:+returnafu_ioctl_dma_unmap(pdata,(void__user*)arg);default:/**Letsub-feature'sioctlfunctiontohandlethecmd
@@ -344,6 +401,7 @@ static int afu_dev_init(struct platform_device *pdev)mutex_lock(&pdata->lock);fpga_pdata_set_private(pdata,afu);afu_region_init(pdata);+afu_dma_region_init(pdata);mutex_unlock(&pdata->lock);return0;}
@@ -356,6 +414,7 @@ static int afu_dev_destroy(struct platform_device *pdev)mutex_lock(&pdata->lock);afu=fpga_pdata_get_private(pdata);afu_region_destroy(pdata);+afu_dma_region_destroy(pdata);fpga_pdata_set_private(pdata,NULL);mutex_unlock(&pdata->lock);
From: Xiao Guangrong <redacted>
User Accelerated Function Unit sub feature exposes the MMIO region of
the AFU. After valid green bitstream (GBS) is programmed and port is
enabled, then this MMIO region could be accessed.
This patch adds support to enumerate the AFU MMIO region and expose it
to userspace via mmap file operation. Below interfaces are exposed to user:
Sysfs interface:
* /sys/class/fpga_region/<regionX>/<fpga-dfl-port.x>/afu_id
Read-only. Indicate which green bitstream is programmed to this AFU.
Ioctl interfaces:
* FPGA_PORT_GET_INFO
Provide info to userspace on the number of supported region.
Only UAFU region is supported now.
* FPGA_PORT_GET_REGION_INFO
Provide region information, including access permission, region size,
offset from the start of device fd.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
------
v2: moved the code to drivers/fpga folder as suggested by Alan Tull.
add sysfs documentation.
switched to GPLv2 license.
v3: rename driver to fpga-dfl-afu
fix coding style and checkpatch issue.
only allow afu_id to be read when port isn't in reset.
---
.../ABI/testing/sysfs-platform-fpga-dfl-afu | 9 +
drivers/fpga/Makefile | 2 +-
drivers/fpga/dfl-afu-main.c | 211 ++++++++++++++++++++-
drivers/fpga/dfl-afu-region.c | 127 +++++++++++++
drivers/fpga/dfl-afu.h | 54 ++++++
include/uapi/linux/fpga-dfl.h | 47 +++++
6 files changed, 446 insertions(+), 4 deletions(-)
create mode 100644 drivers/fpga/dfl-afu-region.c
create mode 100644 drivers/fpga/dfl-afu.h
@@ -5,3 +5,12 @@ Contact: Wu Hao <hao.wu@intel.com> Description: Read-only. It returns id of this port. One DFL FPGA device may have more than one port. Userspace could use this id to distinguish different ports under same FPGA device.++What: /sys/bus/platform/devices/fpga-dfl-port.0/afu_id+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read-only. User can program different green bitstreams (GBS) to+ FPGA Accelerator Function Unit (AFU) for different functions.+ It returns uuid which could be used to identify which GBS is+ programmed in this AFU.
@@ -130,6 +193,64 @@ static long afu_ioctl_check_extension(struct feature_platform_data *pdata,return0;}+staticlong+afu_ioctl_get_info(structfeature_platform_data*pdata,void__user*arg)+{+structfpga_port_infoinfo;+structfpga_afu*afu;+unsignedlongminsz;++minsz=offsetofend(structfpga_port_info,num_umsgs);++if(copy_from_user(&info,arg,minsz))+return-EFAULT;++if(info.argsz<minsz)+return-EINVAL;++mutex_lock(&pdata->lock);+afu=fpga_pdata_get_private(pdata);+info.flags=0;+info.num_regions=afu->num_regions;+info.num_umsgs=afu->num_umsgs;+mutex_unlock(&pdata->lock);++if(copy_to_user(arg,&info,sizeof(info)))+return-EFAULT;++return0;+}++staticlong+afu_ioctl_get_region_info(structfeature_platform_data*pdata,void__user*arg)+{+structfpga_port_region_inforinfo;+structfpga_afu_regionregion;+unsignedlongminsz;+longret;++minsz=offsetofend(structfpga_port_region_info,offset);++if(copy_from_user(&rinfo,arg,minsz))+return-EFAULT;++if(rinfo.argsz<minsz||rinfo.padding)+return-EINVAL;++ret=afu_get_region_by_index(pdata,rinfo.index,®ion);+if(ret)+returnret;++rinfo.flags=region.flags;+rinfo.size=region.size;+rinfo.offset=region.offset;++if(copy_to_user(arg,&rinfo,sizeof(rinfo)))+return-EFAULT;++return0;+}+staticlongafu_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){structplatform_device*pdev=filp->private_data;
@@ -144,6 +265,10 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)returnFPGA_API_VERSION;caseFPGA_CHECK_EXTENSION:returnafu_ioctl_check_extension(pdata,arg);+caseFPGA_PORT_GET_INFO:+returnafu_ioctl_get_info(pdata,(void__user*)arg);+caseFPGA_PORT_GET_REGION_INFO:+returnafu_ioctl_get_region_info(pdata,(void__user*)arg);default:/**Letsub-feature'sioctlfunctiontohandlethecmd
@@ -164,27 +289,106 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)return-EINVAL;}+staticintafu_mmap(structfile*filp,structvm_area_struct*vma)+{+structfpga_afu_regionregion;+structplatform_device*pdev=filp->private_data;+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+u64size=vma->vm_end-vma->vm_start;+u64offset;+intret;++if(!(vma->vm_flags&VM_SHARED))+return-EINVAL;++offset=vma->vm_pgoff<<PAGE_SHIFT;+ret=afu_get_region_by_offset(pdata,offset,size,®ion);+if(ret)+returnret;++if(!(region.flags&FPGA_REGION_MMAP))+return-EINVAL;++if((vma->vm_flags&VM_READ)&&!(region.flags&FPGA_REGION_READ))+return-EPERM;++if((vma->vm_flags&VM_WRITE)&&!(region.flags&FPGA_REGION_WRITE))+return-EPERM;++vma->vm_page_prot=pgprot_noncached(vma->vm_page_prot);++returnremap_pfn_range(vma,vma->vm_start,+(region.phys+(offset-region.offset))>>PAGE_SHIFT,+size,vma->vm_page_prot);+}+staticconststructfile_operationsafu_fops={.owner=THIS_MODULE,.open=afu_open,.release=afu_release,.unlocked_ioctl=afu_ioctl,+.mmap=afu_mmap,};+staticintafu_dev_init(structplatform_device*pdev)+{+structfpga_afu*afu;+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);++afu=devm_kzalloc(&pdev->dev,sizeof(*afu),GFP_KERNEL);+if(!afu)+return-ENOMEM;++afu->pdata=pdata;++mutex_lock(&pdata->lock);+fpga_pdata_set_private(pdata,afu);+afu_region_init(pdata);+mutex_unlock(&pdata->lock);+return0;+}++staticintafu_dev_destroy(structplatform_device*pdev)+{+structfeature_platform_data*pdata=dev_get_platdata(&pdev->dev);+structfpga_afu*afu;++mutex_lock(&pdata->lock);+afu=fpga_pdata_get_private(pdata);+afu_region_destroy(pdata);+fpga_pdata_set_private(pdata,NULL);+mutex_unlock(&pdata->lock);++devm_kfree(&pdev->dev,afu);++return0;+}+staticintafu_probe(structplatform_device*pdev){intret;dev_dbg(&pdev->dev,"%s\n",__func__);+ret=afu_dev_init(pdev);+if(ret)+gotoexit;+ret=fpga_dev_feature_init(pdev,port_feature_drvs);if(ret)-returnret;+gotodev_destroy;ret=fpga_register_dev_ops(pdev,&afu_fops,THIS_MODULE);-if(ret)+if(ret){fpga_dev_feature_uinit(pdev);+gotodev_destroy;+}++return0;+dev_destroy:+afu_dev_destroy(pdev);+exit:returnret;}
@@ -194,6 +398,7 @@ static int afu_remove(struct platform_device *pdev)fpga_dev_feature_uinit(pdev);fpga_unregister_dev_ops(pdev);+afu_dev_destroy(pdev);return0;}
@@ -67,6 +67,53 @@#define FPGA_PORT_RESET _IO(FPGA_MAGIC, PORT_BASE + 0)+/**+*FPGA_PORT_GET_INFO-_IOR(FPGA_MAGIC,PORT_BASE+1,structfpga_port_info)+*+*Retrieveinformationaboutthefpgaport.+*Driverfillstheinfoinprovidedstructfpga_port_info.+*Return:0onsuccess,-errnoonfailure.+*/+structfpga_port_info{+/* Input */+__u32argsz;/* Structure length */+/* Output */+__u32flags;/* Zero for now */+__u32num_regions;/* The number of supported regions */+__u32num_umsgs;/* The number of allocated umsgs */+};++#define FPGA_PORT_GET_INFO _IO(FPGA_MAGIC, PORT_BASE + 1)++/**+*FPGA_PORT_GET_REGION_INFO-_IOWR(FPGA_MAGIC,PORT_BASE+2,+*structfpga_port_region_info)+*+*Retrieveinformationaboutadeviceregion.+*Callerprovidesstructfpga_port_region_infowithindexvalueset.+*Driverreturnstheregioninfoinotherfields.+*Return:0onsuccess,-errnoonfailure.+*/+structfpga_port_region_info{+/* input */+__u32argsz;/* Structure length */+/* Output */+__u32flags;/* Access permission */+#define FPGA_REGION_READ (1 << 0) /* Region is readable */+#define FPGA_REGION_WRITE (1 << 1) /* Region is writable */+#define FPGA_REGION_MMAP (1 << 2) /* Can be mmaped to userspace */+/* Input */+__u32index;/* Region index */+#define FPGA_PORT_INDEX_UAFU 0 /* User AFU */+#define FPGA_PORT_INDEX_STP 1 /* Signal Tap */+__u32padding;+/* Output */+__u64size;/* Region size (bytes) */+__u64offset;/* Region offset from start of device fd */+};++#define FPGA_PORT_GET_REGION_INFO _IO(FPGA_MAGIC, PORT_BASE + 2)+/* IOCTLs for FME file descriptor *//**
The port header register set is always present for port, it is mainly
for capability, control and status of the ports that AFU connected to.
This patch implements header sub feature support. Below user interfaces
are created by this patch.
Sysfs interface:
* /sys/class/fpga_region/<regionX>/<fpga-dfl-port.x>/id
Read-only. Port ID.
Ioctl interface:
* FPGA_PORT_RESET
Reset the FPGA Port and its AFU.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v3: rename driver name to fpga-dfl-afu
add more description for reset ioctl.
fix some checkpatch issues.
---
.../ABI/testing/sysfs-platform-fpga-dfl-afu | 7 ++++
drivers/fpga/dfl-afu-main.c | 44 +++++++++++++++++++++-
include/uapi/linux/fpga-dfl.h | 17 +++++++++
3 files changed, 67 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/sysfs-platform-fpga-dfl-afu
@@ -0,0 +1,7 @@+What: /sys/bus/platform/devices/fpga-dfl-port.0/id+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read-only. It returns id of this port. One DFL FPGA device+ may have more than one port. Userspace could use this id to+ distinguish different ports under same FPGA device.
From: Kang Luwei <redacted>
The header register set is always present for FPGA Management Engine (FME),
this patch implements init and uinit function for header sub feature and
introduce several read-only sysfs interfaces for the capability and status.
Sysfs interfaces:
* /sys/class/fpga_region/<regionX>/<fpga-dfl-fme.x>/ports_num
Read-only. Number of ports implemented
* /sys/class/fpga_region/<regionX>/<fpga-dfl-fme.x>/bitstream_id
Read-only. Blue Bitstream (static FPGA region) identifier number
* /sys/class/fpga_region/<regionX>/<fpga-dfl-fme.x>/bitstream_metadata
Read-only. Blue Bitstream (static FPGA region) meta data
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Kang Luwei <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: add sysfs documentation
v3: rename driver to fpga-dfl-fme.
improve sysfs doc and commit description.
replace bitfield.
---
.../ABI/testing/sysfs-platform-fpga-dfl-fme | 21 ++++++++
drivers/fpga/dfl-fme-main.c | 60 ++++++++++++++++++++++
2 files changed, 81 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-platform-fpga-dfl-fme
@@ -0,0 +1,21 @@+What: /sys/bus/platform/devices/fpga-dfl-fme.0/ports_num+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read-only. One DFL FPGA device may have more than 1+ port/Accelerator Function Unit (AFU). It returns the+ number of ports on the FPGA device when read it.++What: /sys/bus/platform/devices/fpga-dfl-fme.0/bitstream_id+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read-only. It returns Blue Bitstream (static FPGA region)+ identifier number.++What: /sys/bus/platform/devices/fpga-dfl-fme.0/bitstream_meta+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read-only. It returns Blue Bitstream (static FPGA region)+ meta data.
For feature devices, e.g FPGA Management Engine (FME), it may
require fpga_cdev_find_port function to find dedicate port for
further actions, so export this function from feature device
driver module.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v3: s/fpga_for_each_port/fpga_cdev_find_port/
move fpga_cdev_find_port to fpga-dfl module.
---
drivers/fpga/fpga-dfl.c | 27 +++++++++++++++++++++++++++
drivers/fpga/fpga-dfl.h | 16 ++++++++++++++++
2 files changed, 43 insertions(+)
This patch adds region_id to fpga_image_info data structure, it
allows driver to pass region id information to fpga-mgr via
fpga_image_info for fpga reconfiguration function.
Signed-off-by: Wu Hao <redacted>
----
v3: add one line comment for region_id
---
include/linux/fpga/fpga-mgr.h | 2 ++
1 file changed, 2 insertions(+)
From: David Laight <hidden> Date: 2017-11-27 10:27:58
From: Wu Hao
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
David
On Mon, Nov 27, 2017 at 10:28:04AM +0000, David Laight wrote:
From: Wu Hao
quoted
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
Hi David
This patch adds a pcie device driver for Intel FPGA devices which implements
the DFL, e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards. They are pcie devices, and all have DFL implemented in
the MMIO space, so we would like to use one kernel driver to handle them.
With this full patchset, it just provides user the interfaces to configure
and access the FPGA accelerators on Intel DFL based FPGA devices. For sure,
users can develop and build their own logics via tools provided by Intel,
program them to accelerators on these Intel FPGA devices, and access them
for their workloads.
Thanks
Hao
From: Moritz Fischer <mdf@kernel.org> Date: 2017-11-29 06:07:44
Hi Hao,
first pass, I didn't get all the way through, yet.
On Mon, Nov 27, 2017 at 02:42:11PM +0800, Wu Hao wrote:
quoted hunk
Device Feature List (DFL) defines a feature list structure that creates
a link list of feature headers within the MMIO space to provide an
extensible way of adding features. This patch introduces a kernel module
to provide basic infrastructure to support FPGA devices which implement
the Device Feature List.
Usually there will be different features and their sub features linked into
the DFL. This code provides common APIs for feature enumeration, it creates
a container device (FPGA base region), walks through the DFLs and creates
platform devices for feature devices (Currently it only supports two
different feature devices, FPGA Management Engine (FME) and Port which
the Accelerator Function Unit (AFU) connected to). In order to enumerate
the DFLs, the common APIs required low level driver to provide necessary
enumeration information (e.g address for each device feature list for
given device) and fill it to the fpga_enum_info data structure. Please
refer to below description for APIs added for enumeration.
Functions for enumeration information preparation:
*fpga_enum_info_alloc
allocate enumeration information data structure.
*fpga_enum_info_add_dfl
add a device feature list to fpga_enum_info data structure.
*fpga_enum_info_free
free fpga_enum_info data structure and related resources.
Functions for feature device enumeration:
*fpga_enumerate_feature_devs
enumerate feature devices and return container device.
*fpga_remove_feature_devs
remove feature devices under given container device.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Zhang Yi <redacted>
Signed-off-by: Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Signed-off-by: Wu Hao <redacted>
----
v3: split from another patch.
separate dfl enumeration code from original pcie driver.
provide common data structures and APIs for enumeration.
update device feature list parsing process according to latest hw.
add dperf/iperf/hssi sub feature placeholder according to latest hw.
remove build_info_add_sub_feature and other small functions.
replace *_feature_num function with macro.
remove writeq/readq.
---
drivers/fpga/Kconfig | 16 +
drivers/fpga/Makefile | 3 +
drivers/fpga/fpga-dfl.c | 884 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/fpga-dfl.h | 365 ++++++++++++++++++++
4 files changed, 1268 insertions(+)
create mode 100644 drivers/fpga/fpga-dfl.c
create mode 100644 drivers/fpga/fpga-dfl.h
Also I think the current consensus is that this should go in the first
line
+ */
+#include <linux/module.h>
+
+#include "fpga-dfl.h"
+
+static DEFINE_MUTEX(fpga_id_mutex);
+
+enum fpga_id_type {
+ FME_ID, /* fme id allocation and mapping */
+ PORT_ID, /* port id allocation and mapping */
+ FPGA_ID_MAX,
+};
+
+/* it is protected by fpga_id_mutex */
+static struct idr fpga_ids[FPGA_ID_MAX];
+
+static void fpga_ids_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fpga_ids); i++)
+ idr_init(fpga_ids + i);
+}
+
+static void fpga_ids_destroy(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fpga_ids); i++)
+ idr_destroy(fpga_ids + i);
+}
+
+static int alloc_fpga_id(enum fpga_id_type type, struct device *dev)
+{
+ int id;
+
+ WARN_ON(type >= FPGA_ID_MAX);
+ mutex_lock(&fpga_id_mutex);
+ id = idr_alloc(fpga_ids + type, dev, 0, 0, GFP_KERNEL);
+ mutex_unlock(&fpga_id_mutex);
+
+ return id;
+}
+
+static void free_fpga_id(enum fpga_id_type type, int id)
+{
+ WARN_ON(type >= FPGA_ID_MAX);
+ mutex_lock(&fpga_id_mutex);
+ idr_remove(fpga_ids + type, id);
+ mutex_unlock(&fpga_id_mutex);
+}
+
+static enum fpga_id_type feature_dev_id_type(struct platform_device *pdev)
+{
+ if (!strcmp(pdev->name, FPGA_FEATURE_DEV_FME))
+ return FME_ID;
+
+ if (!strcmp(pdev->name, FPGA_FEATURE_DEV_PORT))
+ return PORT_ID;
+
+ WARN_ON(1);
Do we really need a WARN_ON() here? Wouldn't returning an error be
nicer?
+
+ return FPGA_ID_MAX;
+}
+
+/**
+ * build_feature_devs_info - info collected during feature dev build.
+ *
+ * @dev: device to enumerate.
+ * @cdev: the container device for all feature devices.
+ * @feature_dev: current feature device.
+ */
+struct build_feature_devs_info {
+ struct device *dev;
+ struct fpga_cdev *cdev;
+ struct platform_device *feature_dev;
+};
+
+static void fpga_cdev_add_port_dev(struct fpga_cdev *cdev,
+ struct platform_device *port_pdev)
+{
+ struct feature_platform_data *pdata = dev_get_platdata(&port_pdev->dev);
+
+ mutex_lock(&cdev->lock);
+ list_add(&pdata->node, &cdev->port_dev_list);
+ get_device(&pdata->dev->dev);
+ mutex_unlock(&cdev->lock);
+}
+
+/*
+ * register current feature device, it is called when we need to switch to
+ * another feature parsing or we have parsed all features on given device
+ * feature list.
+ */
+static int build_info_commit_dev(struct build_feature_devs_info *binfo)
+{
+ int ret;
+
+ if (!binfo->feature_dev)
+ return 0;
+
+ ret = platform_device_add(binfo->feature_dev);
+ if (!ret) {
+ if (feature_dev_id_type(binfo->feature_dev) == PORT_ID)
+ fpga_cdev_add_port_dev(binfo->cdev, binfo->feature_dev);
+ else
So if you get back FPGA_ID_MAX, it is automatically a fme_dev?
+ binfo->cdev->fme_dev =
+ get_device(&binfo->feature_dev->dev);
+ /*
+ * reset it to avoid build_info_free() freeing their resource.
+ *
+ * The resource of successfully registered feature devices
+ * will be freed by platform_device_unregister(). See the
+ * comments in build_info_create_dev().
+ */
+ binfo->feature_dev = NULL;
+ }
+
+ return ret;
+}
+
+static int
+build_info_create_dev(struct build_feature_devs_info *binfo,
+ enum fpga_id_type type, int feature_nr, const char *name)
+{
+ struct platform_device *fdev;
+ struct resource *res;
+ struct feature_platform_data *pdata;
+ int ret;
+
+ /* we will create a new device, commit current device first */
+ ret = build_info_commit_dev(binfo);
+ if (ret)
+ return ret;
+
+ /*
+ * we use -ENODEV as the initialization indicator which indicates
+ * whether the id need to be reclaimed
+ */
+ fdev = platform_device_alloc(name, -ENODEV);
+ if (!fdev)
+ return -ENOMEM;
+
+ binfo->feature_dev = fdev;
+
+ fdev->id = alloc_fpga_id(type, &fdev->dev);
+ if (fdev->id < 0)
+ return fdev->id;
+
+ fdev->dev.parent = &binfo->cdev->region.dev;
+
+ /*
+ * we do not need to care for the memory which is associated with
+ * the platform device. After calling platform_device_unregister(),
+ * it will be automatically freed by device's release() callback,
+ * platform_device_release().
+ */
+ pdata = kzalloc(feature_platform_data_size(feature_nr), GFP_KERNEL);
+ if (pdata) {
+ pdata->dev = fdev;
+ pdata->num = feature_nr;
+ mutex_init(&pdata->lock);
+ } else {
+ return -ENOMEM;
Does this path clean up fdev->id? Does that happen in
platform_device_release() ?
quoted hunk
+ }
+
+ /*
+ * the count should be initialized to 0 to make sure
+ *__fpga_port_enable() following __fpga_port_disable()
+ * works properly for port device.
+ * and it should always be 0 for fme device.
+ */
+ WARN_ON(pdata->disable_count);
+
+ fdev->dev.platform_data = pdata;
+ fdev->num_resources = feature_nr;
+ fdev->resource = kcalloc(feature_nr, sizeof(*res), GFP_KERNEL);
+ if (!fdev->resource)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void build_info_free(struct build_feature_devs_info *binfo)
+{
+ /*
+ * it is a valid id, free it. See comments in
+ * build_info_create_dev()
+ */
+ if (binfo->feature_dev && binfo->feature_dev->id >= 0)
+ free_fpga_id(feature_dev_id_type(binfo->feature_dev),
+ binfo->feature_dev->id);
+
+ platform_device_put(binfo->feature_dev);
+
+ devm_kfree(binfo->dev, binfo);
+}
+
+/*
+ * UAFU GUID is dynamic as it can be changed after FME downloads different
+ * Green Bitstream to the port, so we treat the unknown GUIDs which are
+ * attached on port's feature list as UAFU.
+ */
+static bool feature_is_UAFU(struct build_feature_devs_info *binfo)
+{
+ if (!binfo->feature_dev ||
+ feature_dev_id_type(binfo->feature_dev) != PORT_ID)
+ return false;
+
+ return true;
+}
+
+struct feature_info {
+ const char *name;
+ resource_size_t resource_size;
+ int feature_index;
+};
+
+/* indexed by fme feature IDs which are defined in 'enum fme_feature_id'. */
+static struct feature_info fme_features[] = {
+ {
+ .name = FME_FEATURE_HEADER,
+ .resource_size = FME_HDR_SIZE,
+ .feature_index = FME_FEATURE_ID_HEADER,
+ },
+ {
+ .name = FME_FEATURE_THERMAL_MGMT,
+ .resource_size = FME_THERMAL_SIZE,
+ .feature_index = FME_FEATURE_ID_THERMAL_MGMT,
+ },
+ {
+ .name = FME_FEATURE_POWER_MGMT,
+ .resource_size = FME_POWER_SIZE,
+ .feature_index = FME_FEATURE_ID_POWER_MGMT,
+ },
+ {
+ .name = FME_FEATURE_GLOBAL_IPERF,
+ .resource_size = FME_IPERF_SIZE,
+ .feature_index = FME_FEATURE_ID_GLOBAL_IPERF,
+ },
+ {
+ .name = FME_FEATURE_GLOBAL_ERR,
+ .resource_size = FME_ERR_SIZE,
+ .feature_index = FME_FEATURE_ID_GLOBAL_ERR,
+ },
+ {
+ .name = FME_FEATURE_PR_MGMT,
+ .resource_size = FME_PR_SIZE,
+ .feature_index = FME_FEATURE_ID_PR_MGMT,
+ },
+ {
+ .name = FME_FEATURE_HSSI,
+ .resource_size = FME_HSSI_SIZE,
+ .feature_index = FME_FEATURE_ID_HSSI,
+ },
+ {
+ .name = FME_FEATURE_GLOBAL_DPERF,
+ .resource_size = FME_DPERF_SIZE,
+ .feature_index = FME_FEATURE_ID_GLOBAL_DPERF,
+ },
+};
+
+/* indexed by port feature IDs which are defined in 'enum port_feature_id'. */
+static struct feature_info port_features[] = {
+ {
+ .name = PORT_FEATURE_HEADER,
+ .resource_size = PORT_HDR_SIZE,
+ .feature_index = PORT_FEATURE_ID_HEADER,
+ },
+ {
+ .name = PORT_FEATURE_ERR,
+ .resource_size = PORT_ERR_SIZE,
+ .feature_index = PORT_FEATURE_ID_ERROR,
+ },
+ {
+ .name = PORT_FEATURE_UMSG,
+ .resource_size = PORT_UMSG_SIZE,
+ .feature_index = PORT_FEATURE_ID_UMSG,
+ },
+ {
+ /* This feature isn't available for now */
+ .name = PORT_FEATURE_PR,
+ .resource_size = DFH_SIZE,
+ .feature_index = PORT_FEATURE_ID_PR,
+ },
+ {
+ .name = PORT_FEATURE_STP,
+ .resource_size = PORT_STP_SIZE,
+ .feature_index = PORT_FEATURE_ID_STP,
+ },
+ {
+ /*
+ * For User AFU feature, its region size is not fixed, but
+ * reported by register PortCapability.mmio_size. Resource
+ * size of UAFU will be set while parse port device.
+ */
+ .name = PORT_FEATURE_UAFU,
+ .resource_size = 0,
+ .feature_index = PORT_FEATURE_ID_UAFU,
+ },
+};
+
+static int
+create_feature_instance(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl, resource_size_t ofst,
+ struct feature_info *finfo)
+{
+ int index = finfo->feature_index;
+ struct platform_device *fdev = binfo->feature_dev;
+ struct feature_platform_data *pdata = dev_get_platdata(&fdev->dev);
+ struct resource *res = &fdev->resource[index];
+
+ if ((dfl->len - ofst < finfo->resource_size) || pdata->num < index)
+ return -EINVAL;
+
+ res->start = dfl->start + ofst;
+ res->end = res->start + finfo->resource_size - 1;
+ res->flags = IORESOURCE_MEM;
+ res->name = finfo->name;
+
+ pdata->features[index].name = finfo->name;
+ pdata->features[index].resource_index = index;
+ pdata->features[index].ioaddr = dfl->ioaddr + ofst;
+
+ return 0;
+}
+
+static int parse_feature_fme(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ int ret;
+
+ ret = build_info_create_dev(binfo, FME_ID, FME_FEATURE_NUM,
+ FPGA_FEATURE_DEV_FME);
+ if (ret)
+ return ret;
+
+ return create_feature_instance(binfo, dfl, ofst,
+ &fme_features[FME_FEATURE_ID_HEADER]);
+}
+
+static int parse_feature_fme_private(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ u64 v;
+ int id;
+
+ v = readq(dfl->ioaddr + ofst + DFH);
+ id = FIELD_GET(DFH_ID, v);
+
+ if (id >= ARRAY_SIZE(fme_features)) {
+ dev_info(binfo->dev, "FME feature id %x is not supported yet.\n",
+ id);
+ return 0;
+ }
+
+ return create_feature_instance(binfo, dfl, ofst, &fme_features[id]);
+}
+
+static int parse_feature_port(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ int ret;
+
+ ret = build_info_create_dev(binfo, PORT_ID, PORT_FEATURE_NUM,
+ FPGA_FEATURE_DEV_PORT);
+ if (ret)
+ return ret;
+
+ return create_feature_instance(binfo, dfl, ofst,
+ &port_features[PORT_FEATURE_ID_HEADER]);
+}
+
+static void enable_port_uafu(struct build_feature_devs_info *binfo)
+{
+ enum port_feature_id id = PORT_FEATURE_ID_UAFU;
+ void __iomem *base;
+ u64 v;
+
+ base = get_feature_ioaddr_by_index(&binfo->feature_dev->dev,
+ PORT_FEATURE_ID_HEADER);
+
+ v = readq(base + PORT_HDR_CAP);
+ port_features[id].resource_size =
+ FIELD_GET(PORT_CAP_MMIO_SIZE, v) << 10;
+
+ /*
+ * To enable User AFU, driver needs to clear reset bit on related port,
+ * otherwise the mmio space of this user AFU will be invalid.
+ */
+ if (port_features[id].resource_size)
+ fpga_port_reset(binfo->feature_dev);
+}
+
+static int parse_feature_port_private(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ enum port_feature_id id;
+ u32 dfh_id;
+ u64 v;
+
+ v = readq(dfl->ioaddr + ofst + DFH);
+ dfh_id = FIELD_GET(DFH_ID, v);
+
+ /*
+ * the region of port feature id is [0x10, 0x13], + 1 to reserve 0
+ * which is dedicated for port-hdr.
+ */
+ id = (dfh_id & 0x000f) + 1;
+
+ if (id >= ARRAY_SIZE(port_features)) {
+ dev_info(binfo->dev, "Port feature id %x is not supported yet.\n",
+ dfh_id);
+ return 0;
+ }
+
+ return create_feature_instance(binfo, dfl, ofst, &port_features[id]);
+}
+
+static int parse_feature_port_uafu(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ enum port_feature_id id = PORT_FEATURE_ID_UAFU;
+ int ret;
+
+ if (port_features[id].resource_size) {
+ ret = create_feature_instance(binfo, dfl, ofst,
+ &port_features[id]);
+ port_features[id].resource_size = 0;
+ } else {
+ dev_err(binfo->dev, "the uafu feature header is mis-configured.\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int parse_feature_afus(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ void __iomem *start = dfl->ioaddr + ofst;
+ void __iomem *end = dfl->ioaddr + dfl->len;
+ u32 offset;
+ u64 v;
+ int ret;
+
+ for (; start < end; start += offset) {
+ if (end - start < AFU_DFH_SIZE)
+ return -EINVAL;
+
+ if (feature_is_UAFU(binfo))
+ ret = parse_feature_port_uafu(binfo, dfl,
+ start - dfl->ioaddr);
+ if (ret)
+ return ret;
+
+ v = readq(start + NEXT_AFU);
+
+ offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v);
+ if (!offset)
+ break;
+ }
+
+ return 0;
+}
+
+static int parse_feature_fiu(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ u32 id, offset;
+ u64 v;
+ int ret = 0;
+
+ v = readq(dfl->ioaddr + ofst + DFH);
+ id = FIELD_GET(DFH_ID, v);
+
+ switch (id) {
+ case DFH_ID_FIU_FME:
+ return parse_feature_fme(binfo, dfl, ofst);
+ case DFH_ID_FIU_PORT:
+ ret = parse_feature_port(binfo, dfl, ofst);
+ enable_port_uafu(binfo);
+ if (ret)
+ return ret;
+
+ /* Check Port FIU's next_afu pointer to User AFU DFH */
+ v = readq(dfl->ioaddr + ofst + NEXT_AFU);
+
+ offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v);
+ if (offset)
+ return parse_feature_afus(binfo, dfl, ofst + offset);
+
+ dev_dbg(binfo->dev, "No AFUs detected on Port\n");
+ break;
+ default:
+ dev_info(binfo->dev, "FIU TYPE %d is not supported yet.\n",
+ id);
+ }
+
+ return ret;
+}
+
+static int parse_feature_private(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ u64 v;
+ u32 id;
+
+ v = readq(dfl->ioaddr + ofst + DFH);
+ id = FIELD_GET(DFH_ID, v);
+
+ if (!binfo->feature_dev) {
+ dev_err(binfo->dev, "the private feature %x does not belong to any AFU.\n",
+ id);
+ return -EINVAL;
+ }
+
+ switch (feature_dev_id_type(binfo->feature_dev)) {
+ case FME_ID:
+ return parse_feature_fme_private(binfo, dfl, ofst);
+ case PORT_ID:
+ return parse_feature_port_private(binfo, dfl, ofst);
+ default:
+ dev_info(binfo->dev, "private feature %x belonging to AFU %s is not supported yet.\n",
+ id, binfo->feature_dev->name);
+ }
+ return 0;
+}
+
+/**
+ * parse_feature - parse a feature on given device feature list
+ *
+ * @binfo: build feature devices information.
+ * @dfl: device feature list to parse
+ * @ofst: offset to feature header on this device feature list
+ */
+static int parse_feature(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl, resource_size_t ofst)
+{
+ u64 v;
+ u32 type;
+
+ v = readq(dfl->ioaddr + ofst + DFH);
+ type = FIELD_GET(DFH_TYPE, v);
+
+ switch (type) {
+ case DFH_TYPE_AFU:
+ return parse_feature_afus(binfo, dfl, ofst);
+ case DFH_TYPE_PRIVATE:
+ return parse_feature_private(binfo, dfl, ofst);
+ case DFH_TYPE_FIU:
+ return parse_feature_fiu(binfo, dfl, ofst);
+ default:
+ dev_info(binfo->dev,
+ "Feature Type %x is not supported.\n", type);
+ }
+
+ return 0;
+}
+
+static int parse_feature_list(struct build_feature_devs_info *binfo,
+ struct fpga_enum_dfl *dfl)
+{
+ void __iomem *start = dfl->ioaddr;
+ void __iomem *end = dfl->ioaddr + dfl->len;
+ int ret = 0;
+ u32 ofst = 0;
+ u64 v;
+
+ /* walk through the device feature list via DFH's next DFH pointer. */
+ for (; start < end; start += ofst) {
+ if (end - start < DFH_SIZE) {
+ dev_err(binfo->dev, "The region is too small to contain a feature.\n");
+ return -EINVAL;
+ }
+
+ ret = parse_feature(binfo, dfl, start - dfl->ioaddr);
+ if (ret)
+ return ret;
+
+ v = readq(start + DFH);
+ ofst = FIELD_GET(DFH_NEXT_HDR_OFST, v);
+
+ /* stop parsing if EOL(End of List) is set or offset is 0 */
+ if ((v & DFH_EOL) || !ofst)
+ break;
+ }
+
+ /* commit current feature device when reach the end of list */
+ return build_info_commit_dev(binfo);
+}
+
+struct fpga_enum_info *fpga_enum_info_alloc(struct device *dev)
+{
+ struct fpga_enum_info *info;
+
+ get_device(dev);
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ put_device(dev);
+ return NULL;
+ }
+
+ info->dev = dev;
+ INIT_LIST_HEAD(&info->dfls);
+
+ return info;
+}
+EXPORT_SYMBOL_GPL(fpga_enum_info_alloc);
+
+int fpga_enum_info_add_dfl(struct fpga_enum_info *info, resource_size_t start,
+ resource_size_t len, void __iomem *ioaddr)
+{
+ struct fpga_enum_dfl *dfl;
+
+ dfl = devm_kzalloc(info->dev, sizeof(*dfl), GFP_KERNEL);
+ if (!dfl)
+ return -ENOMEM;
+
+ dfl->start = start;
+ dfl->len = len;
+ dfl->ioaddr = ioaddr;
+
+ list_add_tail(&dfl->node, &info->dfls);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fpga_enum_info_add_dfl);
+
+void fpga_enum_info_free(struct fpga_enum_info *info)
+{
+ struct fpga_enum_dfl *tmp, *dfl;
+ struct device *dev;
+
+ if (!info)
+ return;
+
+ dev = info->dev;
+
+ /* remove all device feature lists in the list. */
+ list_for_each_entry_safe(dfl, tmp, &info->dfls, node) {
+ list_del(&dfl->node);
+ devm_kfree(dev, dfl);
+ }
+
+ devm_kfree(dev, info);
+ put_device(dev);
+}
+EXPORT_SYMBOL_GPL(fpga_enum_info_free);
+
+static int remove_feature_dev(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ enum fpga_id_type type = feature_dev_id_type(pdev);
+ int id = pdev->id;
+
+ platform_device_unregister(pdev);
+
+ free_fpga_id(type, id);
+
+ return 0;
+}
+
+static void remove_feature_devs(struct fpga_cdev *cdev)
+{
+ device_for_each_child(&cdev->region.dev, NULL, remove_feature_dev);
+}
+
+/**
+ * fpga_enumerate_feature_devs - enumerate feature devices
+ * @info: information for enumeration.
+ *
+ * This function creates a container device (base FPGA region), enumerates
+ * feature devices based on the enumeration info and creates platform devices
+ * under the container device.
+ *
+ * Return: fpga_cdev struct on success, -errno on failure
+ */
+struct fpga_cdev *fpga_enumerate_feature_devs(struct fpga_enum_info *info)
+{
+ struct build_feature_devs_info *binfo;
+ struct fpga_cdev *cdev;
+ struct fpga_enum_dfl *dfl;
+ int ret = 0;
+
+ if (!info->dev)
+ return ERR_PTR(-ENODEV);
+
+ cdev = devm_kzalloc(info->dev, sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return ERR_PTR(-ENOMEM);
+
+ cdev->parent = info->dev;
+ mutex_init(&cdev->lock);
+ INIT_LIST_HEAD(&cdev->port_dev_list);
+ cdev->region.parent = info->dev;
+
+ ret = fpga_region_register(&cdev->region);
+ if (ret)
+ goto free_cdev_exit;
+
+ /* create and init build info for enumeration */
+ binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
+ if (!binfo) {
+ ret = -ENOMEM;
+ goto unregister_region_exit;
+ }
+
+ binfo->dev = info->dev;
+ binfo->cdev = cdev;
+
+ /*
+ * start enumeration for all feature devices based on Device Feature
+ * Lists.
+ */
+ list_for_each_entry(dfl, &info->dfls, node) {
+ ret = parse_feature_list(binfo, dfl);
+ if (ret) {
+ remove_feature_devs(cdev);
+ build_info_free(binfo);
+ goto unregister_region_exit;
+ }
+ }
+
+ build_info_free(binfo);
+
+ return cdev;
+
+unregister_region_exit:
+ fpga_region_unregister(&cdev->region);
+free_cdev_exit:
+ devm_kfree(cdev->parent, cdev);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(fpga_enumerate_feature_devs);
+
+/**
+ * fpga_remove_feature_devs - remove all feature devices
+ * @cdev: fpga container device.
+ *
+ * Remove the container device and all feature devices under given container
+ * devices.
+ */
+void fpga_remove_feature_devs(struct fpga_cdev *cdev)
+{
+ struct feature_platform_data *pdata, *ptmp;
+
+ remove_feature_devs(cdev);
+
+ mutex_lock(&cdev->lock);
+ if (cdev->fme_dev) {
+ /* the fme should be unregistered. */
+ WARN_ON(device_is_registered(cdev->fme_dev));
+ put_device(cdev->fme_dev);
+ }
+
+ list_for_each_entry_safe(pdata, ptmp, &cdev->port_dev_list, node) {
+ struct platform_device *port_dev = pdata->dev;
+
+ /* the port should be unregistered. */
+ WARN_ON(device_is_registered(&port_dev->dev));
+ list_del(&pdata->node);
+ put_device(&port_dev->dev);
+ }
+ mutex_unlock(&cdev->lock);
+
+ fpga_region_unregister(&cdev->region);
+ devm_kfree(cdev->parent, cdev);
+}
+EXPORT_SYMBOL_GPL(fpga_remove_feature_devs);
+
+int fpga_port_id(struct platform_device *pdev)
+{
+ void __iomem *base;
+
+ base = get_feature_ioaddr_by_index(&pdev->dev, PORT_FEATURE_ID_HEADER);
+ WARN_ON(!base);
+
+ return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + FME_HDR_CAP));
+}
+EXPORT_SYMBOL_GPL(fpga_port_id);
+
+/*
+ * Enable Port by clear the port soft reset bit, which is set by default.
+ * The User AFU is unable to respond to any MMIO access while in reset.
+ * __fpga_port_enable function should only be used after __fpga_port_disable
+ * function.
+ */
+void __fpga_port_enable(struct platform_device *pdev)
+{
+ struct feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ void __iomem *base;
+ u64 v;
+
+ WARN_ON(!pdata->disable_count);
+
+ if (--pdata->disable_count != 0)
+ return;
+
+ base = get_feature_ioaddr_by_index(&pdev->dev, PORT_FEATURE_ID_HEADER);
+ WARN_ON(!base);
+
+ /* Clear port soft reset */
+ v = readq(base + PORT_HDR_CTRL);
+ v &= ~PORT_CTRL_SFTRST;
+ writeq(v, base + PORT_HDR_CTRL);
+}
+EXPORT_SYMBOL_GPL(__fpga_port_enable);
+
+#define RST_POLL_INVL 10 /* us */
+#define RST_POLL_TIMEOUT 1000 /* us */
+
+int __fpga_port_disable(struct platform_device *pdev)
+{
+ struct feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ void __iomem *base;
+ u64 v;
+
+ if (pdata->disable_count++ != 0)
+ return 0;
+
+ base = get_feature_ioaddr_by_index(&pdev->dev, PORT_FEATURE_ID_HEADER);
+ WARN_ON(!base);
+
+ /* Set port soft reset */
+ v = readq(base + PORT_HDR_CTRL);
+ v |= PORT_CTRL_SFTRST;
+ writeq(v, base + PORT_HDR_CTRL);
+
+ /*
+ * HW sets ack bit to 1 when all outstanding requests have been drained
+ * on this port and minimum soft reset pulse width has elapsed.
+ * Driver polls port_soft_reset_ack to determine if reset done by HW.
+ */
+ if (readq_poll_timeout(base + PORT_HDR_CTRL, v, v & PORT_CTRL_SFTRST,
+ RST_POLL_INVL, RST_POLL_TIMEOUT)) {
+ dev_err(&pdev->dev, "timeout, fail to reset device\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__fpga_port_disable);
+
+static int __init dfl_fpga_init(void)
+{
+ fpga_ids_init();
+
+ return 0;
+}
+
+static void __exit dfl_fpga_exit(void)
+{
+ fpga_ids_destroy();
+}
+
+module_init(dfl_fpga_init);
+module_exit(dfl_fpga_exit);
+
+MODULE_DESCRIPTION("FPGA Device Feature List (DFL) Support");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
From: Moritz Fischer <mdf@kernel.org> Date: 2017-11-29 06:11:12
Hi Hao,
On Mon, Nov 27, 2017 at 02:42:09PM +0800, Wu Hao wrote:
This patch adds region_id to fpga_image_info data structure, it
allows driver to pass region id information to fpga-mgr via
fpga_image_info for fpga reconfiguration function.
Signed-off-by: Wu Hao <redacted>
Acked-by: Moritz Fischer <redacted>
quoted hunk
----
v3: add one line comment for region_id
---
include/linux/fpga/fpga-mgr.h | 2 ++
1 file changed, 2 insertions(+)
On Tue, Nov 28, 2017 at 10:07:36PM -0800, Moritz Fischer wrote:
Hi Hao,
first pass, I didn't get all the way through, yet.
Hi Moritz
Thanks a lot for your review and comments. :)
On Mon, Nov 27, 2017 at 02:42:11PM +0800, Wu Hao wrote:
quoted
Device Feature List (DFL) defines a feature list structure that creates
a link list of feature headers within the MMIO space to provide an
extensible way of adding features. This patch introduces a kernel module
to provide basic infrastructure to support FPGA devices which implement
the Device Feature List.
Usually there will be different features and their sub features linked into
the DFL. This code provides common APIs for feature enumeration, it creates
a container device (FPGA base region), walks through the DFLs and creates
platform devices for feature devices (Currently it only supports two
different feature devices, FPGA Management Engine (FME) and Port which
the Accelerator Function Unit (AFU) connected to). In order to enumerate
the DFLs, the common APIs required low level driver to provide necessary
enumeration information (e.g address for each device feature list for
given device) and fill it to the fpga_enum_info data structure. Please
refer to below description for APIs added for enumeration.
Functions for enumeration information preparation:
*fpga_enum_info_alloc
allocate enumeration information data structure.
*fpga_enum_info_add_dfl
add a device feature list to fpga_enum_info data structure.
*fpga_enum_info_free
free fpga_enum_info data structure and related resources.
Functions for feature device enumeration:
*fpga_enumerate_feature_devs
enumerate feature devices and return container device.
*fpga_remove_feature_devs
remove feature devices under given container device.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Zhang Yi <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v3: split from another patch.
separate dfl enumeration code from original pcie driver.
provide common data structures and APIs for enumeration.
update device feature list parsing process according to latest hw.
add dperf/iperf/hssi sub feature placeholder according to latest hw.
remove build_info_add_sub_feature and other small functions.
replace *_feature_num function with macro.
remove writeq/readq.
---
drivers/fpga/Kconfig | 16 +
drivers/fpga/Makefile | 3 +
drivers/fpga/fpga-dfl.c | 884 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/fpga-dfl.h | 365 ++++++++++++++++++++
4 files changed, 1268 insertions(+)
create mode 100644 drivers/fpga/fpga-dfl.c
create mode 100644 drivers/fpga/fpga-dfl.h
Also I think the current consensus is that this should go in the first
line
Sure, I will put this SPDX-License-Identifier to the first line and remove
the redundant line above.
quoted
+ */
+#include <linux/module.h>
+
+#include "fpga-dfl.h"
+
+static DEFINE_MUTEX(fpga_id_mutex);
+
+enum fpga_id_type {
+ FME_ID, /* fme id allocation and mapping */
+ PORT_ID, /* port id allocation and mapping */
+ FPGA_ID_MAX,
+};
+
+/* it is protected by fpga_id_mutex */
+static struct idr fpga_ids[FPGA_ID_MAX];
+
+static void fpga_ids_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fpga_ids); i++)
+ idr_init(fpga_ids + i);
+}
+
+static void fpga_ids_destroy(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fpga_ids); i++)
+ idr_destroy(fpga_ids + i);
+}
+
+static int alloc_fpga_id(enum fpga_id_type type, struct device *dev)
+{
+ int id;
+
+ WARN_ON(type >= FPGA_ID_MAX);
+ mutex_lock(&fpga_id_mutex);
+ id = idr_alloc(fpga_ids + type, dev, 0, 0, GFP_KERNEL);
+ mutex_unlock(&fpga_id_mutex);
+
+ return id;
+}
+
+static void free_fpga_id(enum fpga_id_type type, int id)
+{
+ WARN_ON(type >= FPGA_ID_MAX);
+ mutex_lock(&fpga_id_mutex);
+ idr_remove(fpga_ids + type, id);
+ mutex_unlock(&fpga_id_mutex);
+}
+
+static enum fpga_id_type feature_dev_id_type(struct platform_device *pdev)
+{
+ if (!strcmp(pdev->name, FPGA_FEATURE_DEV_FME))
+ return FME_ID;
+
+ if (!strcmp(pdev->name, FPGA_FEATURE_DEV_PORT))
+ return PORT_ID;
+
+ WARN_ON(1);
Do we really need a WARN_ON() here? Wouldn't returning an error be
nicer?
Actually this is a static function only used in this file, and ideally
enumeration code will only create feature devices for FME and PORT.
So ideally there couldn't be any feature device with other names, only FME
and PORT. WARN_ON(1) is used here, to just be a warning that there could
be a critical driver problem somewhere and return FPGA_ID_MAX as error code
which will never be used as an id for any feature device.
quoted
+
+ return FPGA_ID_MAX;
+}
+
+/**
+ * build_feature_devs_info - info collected during feature dev build.
+ *
+ * @dev: device to enumerate.
+ * @cdev: the container device for all feature devices.
+ * @feature_dev: current feature device.
+ */
+struct build_feature_devs_info {
+ struct device *dev;
+ struct fpga_cdev *cdev;
+ struct platform_device *feature_dev;
+};
+
+static void fpga_cdev_add_port_dev(struct fpga_cdev *cdev,
+ struct platform_device *port_pdev)
+{
+ struct feature_platform_data *pdata = dev_get_platdata(&port_pdev->dev);
+
+ mutex_lock(&cdev->lock);
+ list_add(&pdata->node, &cdev->port_dev_list);
+ get_device(&pdata->dev->dev);
+ mutex_unlock(&cdev->lock);
+}
+
+/*
+ * register current feature device, it is called when we need to switch to
+ * another feature parsing or we have parsed all features on given device
+ * feature list.
+ */
+static int build_info_commit_dev(struct build_feature_devs_info *binfo)
+{
+ int ret;
+
+ if (!binfo->feature_dev)
+ return 0;
+
+ ret = platform_device_add(binfo->feature_dev);
+ if (!ret) {
+ if (feature_dev_id_type(binfo->feature_dev) == PORT_ID)
+ fpga_cdev_add_port_dev(binfo->cdev, binfo->feature_dev);
+ else
So if you get back FPGA_ID_MAX, it is automatically a fme_dev?
If FPGA_ID_MAX returned, there would be a WARN_ON(1) triggered, so we get
notified there would be a critical driver issue somewhere, needs to be fixed
firstly.
In normal driver flow, we should never see FPGA_ID_MAX returned and the
WARN_ON(1). :)
quoted
+ binfo->cdev->fme_dev =
+ get_device(&binfo->feature_dev->dev);
+ /*
+ * reset it to avoid build_info_free() freeing their resource.
+ *
+ * The resource of successfully registered feature devices
+ * will be freed by platform_device_unregister(). See the
+ * comments in build_info_create_dev().
+ */
+ binfo->feature_dev = NULL;
+ }
+
+ return ret;
+}
+
+static int
+build_info_create_dev(struct build_feature_devs_info *binfo,
+ enum fpga_id_type type, int feature_nr, const char *name)
+{
+ struct platform_device *fdev;
+ struct resource *res;
+ struct feature_platform_data *pdata;
+ int ret;
+
+ /* we will create a new device, commit current device first */
+ ret = build_info_commit_dev(binfo);
+ if (ret)
+ return ret;
+
+ /*
+ * we use -ENODEV as the initialization indicator which indicates
+ * whether the id need to be reclaimed
+ */
+ fdev = platform_device_alloc(name, -ENODEV);
+ if (!fdev)
+ return -ENOMEM;
+
+ binfo->feature_dev = fdev;
+
+ fdev->id = alloc_fpga_id(type, &fdev->dev);
+ if (fdev->id < 0)
+ return fdev->id;
+
+ fdev->dev.parent = &binfo->cdev->region.dev;
+
+ /*
+ * we do not need to care for the memory which is associated with
+ * the platform device. After calling platform_device_unregister(),
+ * it will be automatically freed by device's release() callback,
+ * platform_device_release().
+ */
+ pdata = kzalloc(feature_platform_data_size(feature_nr), GFP_KERNEL);
+ if (pdata) {
+ pdata->dev = fdev;
+ pdata->num = feature_nr;
+ mutex_init(&pdata->lock);
+ } else {
+ return -ENOMEM;
Does this path clean up fdev->id? Does that happen in
platform_device_release() ?
This patch cleans up fdev->id manually, as platform_device_release can't
cover this. There are two cases, we have to clean up the fdev->id.
1) error found during enumeration (fdev is not registered yet), just like
above case, return -ENOMEM, and finally it causes parse_feature_list
function to return error code, and fdev->id will be cleaned up by
build_info_free function. (only platform_device_put required as
platform_device_add is not invoked yet).
2) normal clean up flow with registered fdev. Then fdev->id will be
cleaned up by remove_feature_dev function. (platform_device_unregister
will be used in this case). :)
Thanks
Hao
From: Alan Tull <atull@kernel.org> Date: 2017-12-04 19:47:43
On Mon, Nov 27, 2017 at 9:15 PM, Wu Hao [off-list ref] wrote:
On Mon, Nov 27, 2017 at 10:28:04AM +0000, David Laight wrote:
quoted
From: Wu Hao
quoted
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
Hi David
This patch adds a pcie device driver for Intel FPGA devices which implements
the DFL, e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards. They are pcie devices, and all have DFL implemented in
the MMIO space, so we would like to use one kernel driver to handle them.
With this full patchset, it just provides user the interfaces to configure
and access the FPGA accelerators on Intel DFL based FPGA devices. For sure,
users can develop and build their own logics via tools provided by Intel,
program them to accelerators on these Intel FPGA devices, and access them
for their workloads.
I don't see anything Intel specific here. This could all be named dfl-*
Alan
From: Alan Tull <atull@kernel.org> Date: 2017-12-04 19:56:22
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
quoted hunk
Add a document for Intel FPGA driver overview.
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
----
v2: added FME fpga-mgr/bridge/region platform driver to driver organization.
updated open discussion per current implementation.
fixed some typos.
v3: use FPGA base region as container device instead of fpga-dev class.
split common enumeration code from pcie driver to functions exposed by
device feature list framework.
update FME performance reporting which supports both integrated (iperf/)
and discrete (dperf/) FPGA solutions.
---
Documentation/fpga/intel-fpga.txt | 261 ++++++++++++++++++++++++++++++++++++++
1 file changed, 261 insertions(+)
create mode 100644 Documentation/fpga/intel-fpga.txt
This doesn't look Intel specific to me. This could all be 'DFL FPGA Framework'
+-------------------------------------------------------------------------------
+ Enno Luebbers [off-list ref]
+ Xiao Guangrong [off-list ref]
+ Wu Hao [off-list ref]
+
+The Intel FPGA driver provides interfaces for userspace applications to
+configure, enumerate, open, and access FPGA accelerators on platforms equipped
+with Intel(R) FPGA PCIe based solutions and enables system level management
+functions such as FPGA reconfiguration, power management, and virtualization.
+
+HW Architecture
+===============
+From the OS's point of view, the FPGA hardware appears as a regular PCIe device.
+The FPGA device memory is organized using a predefined data structure (Device
+Feature List). Features supported by the particular FPGA device are exposed
+through these data structures, as illustrated below:
+
+ +-------------------------------+ +-------------+
+ | PF | | VF |
+ +-------------------------------+ +-------------+
+ ^ ^ ^ ^
+ | | | |
++-----|------------|---------|--------------|-------+
+| | | | | |
+| +-----+ +-------+ +-------+ +-------+ |
+| | FME | | Port0 | | Port1 | | Port2 | |
+| +-----+ +-------+ +-------+ +-------+ |
+| ^ ^ ^ |
+| | | | |
+| +-------+ +------+ +-------+ |
+| | AFU | | AFU | | AFU | |
+| +-------+ +------+ +-------+ |
+| |
+| FPGA PCIe Device |
++---------------------------------------------------+
+
+The driver supports PCIe SR-IOV to create virtual functions (VFs) which can be
+used to assign individual accelerators to virtual machines.
+
+FME (FPGA Management Engine)
+============================
+The FPGA Management Engine performs power and thermal management, error
+reporting, reconfiguration, performance reporting for integrated and discrete
+solution, and other infrastructure functions. Each FPGA has one FME, which is
+always accessed through the physical function (PF).
+
+User-space applications can acquire exclusive access to the FME using open(),
+and release it using close().
+
+The following functions are exposed through ioctls:
+
+ Get driver API version (FPGA_GET_API_VERSION)
+ Check for extensions (FPGA_CHECK_EXTENSION)
+ Assign port to PF (FPGA_FME_PORT_ASSIGN)
+ Release port from PF (FPGA_FME_PORT_RELEASE)
+ Program bitstream (FPGA_FME_PORT_PR)
+
+More functions are exposed through sysfs
+(/sys/class/fpga_region/regionX/fpga-dfl-fme.n/):
I see that /sys/class/fpga/* has changed to /sys/class/fpga_region/*
now as requested (thanks!). It looks like it ended up being pretty
straightforward (so far, just diffing this doc with the previous v2).
+
+ Read bitstream ID (bitstream_id)
+ Read bitstream metadata (bitstream_metadata)
+ Read number of ports (ports_num)
+ Read socket ID (socket_id)
+ Read performance counters (iperf/ and dperf/)
+ Power management (power_mgmt/)
+ Thermal management (thermal_mgmt/)
+ Error reporting (errors/)
+
+PORT
+====
+A port represents the interface between the static FPGA fabric (the "blue
+bitstream") and a partially reconfigurable region containing an AFU (the "green
+bitstream"). It controls the communication from SW to the accelerator and
+exposes features such as reset and debug.
+
+A PCIe device may have several ports and each port can be released from PF by
+FPGA_FME_PORT_RELEASE ioctl on FME, and exposed through a VF via PCIe sriov
+sysfs interface.
+
+AFU
+===
+An AFU is attached to a port and exposes a 256k MMIO region to be used for
+accelerator-specific control registers.
+
+User-space applications can acquire exclusive access to an AFU attached to a
+port by using open() on the port device node, and release it using close().
+
+The following functions are exposed through ioctls:
+
+ Get driver API version (FPGA_GET_API_VERSION)
+ Check for extensions (FPGA_CHECK_EXTENSION)
+ Get port info (FPGA_PORT_GET_INFO)
+ Get MMIO region info (FPGA_PORT_GET_REGION_INFO)
+ Map DMA buffer (FPGA_PORT_DMA_MAP)
+ Unmap DMA buffer (FPGA_PORT_DMA_UNMAP)
+ Reset AFU (FPGA_PORT_RESET)
+ Enable UMsg (FPGA_PORT_UMSG_ENABLE)
+ Disable UMsg (FPGA_PORT_UMSG_DISABLE)
+ Set UMsg mode (FPGA_PORT_UMSG_SET_MODE)
+ Set UMsg base address (FPGA_PORT_UMSG_SET_BASE_ADDR)
+
+User-space applications can also mmap() accelerator MMIO regions.
+
+More functions are exposed through sysfs:
+(/sys/class/fpga_region/<regionX>/<fpga-dfl-port.m>/):
+
+ Read Accelerator GUID (afu_id)
+ Error reporting (errors/)
+
+Partial Reconfiguration
+=======================
+As mentioned above, accelerators can be reconfigured through partial
+reconfiguration of a green bitstream file (GBS). The green bitstream must have
+been generated for the exact blue bitstream and targeted reconfigurable region
+(port) of the FPGA; otherwise, the reconfiguration operation will fail and
+possibly cause system instability. This compatibility can be checked by
+comparing the interface ID noted in the GBS header against the interface ID
+exposed by the FME through sysfs (see above). This check is usually done by
+user-space before calling the reconfiguration IOCTL.
+
+FPGA virtualization
+===================
+To enable accessing an accelerator from applications running in a VM, the
+respective AFU's port needs to be assigned to a VF using the following steps:
+
+ a) The PF owns all AFU ports by default. Any port that needs to be reassigned
+ to a VF must first be released through the FPGA_FME_PORT_RELEASE ioctl on the
+ FME device.
+
+ b) Once N ports are released from PF, then user can use command below to
+ enable SRIOV and VFs. Each VF owns only one Port with AFU.
+
+ echo N > $PCI_DEVICE_PATH/sriov_numvfs
+
+ c) Pass through the VFs to VMs
+
+ d) The AFU under VF is accessible from applications in VM (using the same
+ driver inside the VF).
+
+Note that an FME can't be assigned to a VF, thus PR and other management
+functions are only available via the PF.
+
+
+Driver organization
+===================
+
+ +-------++------++------+ |
+ | FME || FME || FME | |
+ | FPGA || FPGA || FPGA | |
+ |Manager||Bridge||Region| |
+ +-------++------++------+ |
+ +-----------------------+ +--------+ | +--------+
+ | FME | | AFU | | | AFU |
+ | Module | | Module | | | Module |
+ +-----------------------+ +--------+ | +--------+
+ +-----------------------+ | +-----------------------+
+ | FPGA Container Device | | | FPGA Container Device |
+ | (FPGA Base Region) | | | (FPGA Base Region) |
+ +-----------------------+ | +-----------------------+
+ +------------------+ | +------------------+
+ | FPGA PCIE Module | | Virtual | FPGA PCIE Module |
+ +------------------+ Host | Machine +------------------+
+ -------------------------------------- | ------------------------------
+ +---------------+ | +---------------+
+ | PCI PF Device | | | PCI VF Device |
+ +---------------+ | +---------------+
+
+The FPGA devices appear as regular PCIe devices; thus, the FPGA PCIe device
+driver is always loaded first once a FPGA PCIE PF or VF device is detected. This
+driver plays an infrastructural role in the driver architecture. It:
+
+ a) locates the Device Feature Lists in PCIE device BAR memory, handles
+ them and related resources to common interfaces from DFL framework
+ for enumeration.
+ b) supports SRIOV.
+
+The feature device infrastructure provides common interfaces to create container
+device (FPGA base region), discover feature devices and their sub features from
+the given Device Feature Lists, and create platform devices for feature devices
+with related resources under the container device. It also abstracts operations
+for sub features and exposes common interfaces to feature device drivers.
+
+The FPGA Management Engine (FME) driver is a platform driver which is loaded
+automatically after FME platform device creation from the PCIE driver. It
+provides the key features for FPGA management, including:
+
+ a) Power and thermal management, error reporting, performance reporting
+ and other infrastructure functions. Users can access these functions
+ via sysfs interfaces exposed by FME driver.
+ b) Partial Reconfiguration. The FME driver creates FPGA manager, FPGA
+ bridges and FPGA regions during PR sub feature initialization; Once
+ it receives an FPGA_FME_PORT_PR ioctl from user, it invokes the
+ common interface function from FPGA Region to complete the partial
+ reconfiguration of the bitstream to the given port.
+ c) Port management for virtualization. The FME driver introduces two
+ ioctls, FPGA_FME_PORT_RELEASE (releases given port from PF) and
+ FPGA_FME_PORT_ASSIGN (assigns the port back to PF). Once the port is
+ released from the PF, it can be assigned to the VF through the SRIOV
+ interfaces provided by PCIE driver. (Refer to "FPGA virtualization"
+ for more details).
+
+Similar to the the FME driver, the FPGA Accelerated Function Unit (AFU) driver
+is probed once the AFU platform device is created. The main function of this
+module is to provide an interface for userspace applications to access the
+individual accelerators, including basic reset control on port, AFU MMIO region
+export, dma buffer mapping service, UMsg notification, and remote debug
+functions (see above).
+
+
+Device enumeration
+==================
+This section introduces how applications enumerate the fpga device from
+the sysfs hierarchy under /sys/class/fpga_region.
+
+In the example below, two Intel(R) FPGA devices are installed in the host. Each
+fpga device has one FME and two ports (AFUs).
+
+FPGA regions are created under /sys/class/fpga_region/
+
+ /sys/class/fpga_region/region0
+ /sys/class/fpga_region/region1
+ /sys/class/fpga_region/region2
+ ...
+
+Application needs to search each regionX folder, if feature device is found,
+(e.g "fpga-dfl-port.n" or "fpga-dfl-fme.m" is found), then it's the base
+fpga region which represents the FPGA device.
+
+Each base region has one FME and two ports (AFUs) as child devices:
+
+ /sys/class/fpga_region/region0/fpga-dfl-fme.0
+ /sys/class/fpga_region/region0/fpga-dfl-port.0
+ /sys/class/fpga_region/region0/fpga-dfl-port.1
+ ...
+
+ /sys/class/fpga_region/region3/fpga-dfl-fme.1
+ /sys/class/fpga_region/region3/fpga-dfl-port.2
+ /sys/class/fpga_region/region3/fpga-dfl-port.3
+ ...
+
+In general, the FME/AFU sysfs interfaces are named as follows:
+
+ /sys/class/fpga_region/<regionX>/<fpga-dfl-fme.n>/
+ /sys/class/fpga_region/<regionX>/<fpga-dfl-port.m>/
+
+with 'n' consecutively numbering all FMEs and 'm' consecutively numbering all
+ports.
+
+The device nodes used for ioctl() or mmap() can be referenced through:
+
+ /sys/class/fpga_region/<regionX>/<fpga-dfl-fme.n>/dev
+ /sys/class/fpga_region/<regionX>/<fpga-dfl-port.n>/dev
+
+Open discussion
+===============
+FME driver exports one ioctl (FPGA_FME_PORT_PR) for partial reconfiguration to
+user now. In the future, if unified user interfaces for reconfiguration are
+added, FME driver should switch to them from ioctl interface.
--
1.8.3.1
From: Alan Tull <atull@kernel.org> Date: 2017-12-04 20:26:57
On Wed, Nov 29, 2017 at 12:11 AM, Moritz Fischer [off-list ref] wrote:
Hi Hao,
On Mon, Nov 27, 2017 at 02:42:09PM +0800, Wu Hao wrote:
quoted
This patch adds region_id to fpga_image_info data structure, it
allows driver to pass region id information to fpga-mgr via
fpga_image_info for fpga reconfiguration function.
Signed-off-by: Wu Hao <redacted>
Acked-by: Moritz Fischer <mdf@kernel.org>
Acked-by: Alan Tull <atull@kernel.org>
quoted
----
v3: add one line comment for region_id
---
include/linux/fpga/fpga-mgr.h | 2 ++
1 file changed, 2 insertions(+)
From: Alan Tull <atull@kernel.org> Date: 2017-12-04 20:56:27
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
mgr->status isn't used anywhere except in status_show. So we don't
need to add status to the fpga_manager struct. Also don't need the
inline function to update it. Just read the status in status_show, if
that ops exists. If mops->status is NULL, return an error, as below.
This will simplify things and make sure that status_show gets status
that is not stale at all.
quoted hunk
This patch adds status to fpga-manager data structure, to allow
driver to store full/partial reconfiguration errors and other
status information, and adds one status callback to fpga_manager_ops
to allow fpga_manager to collect latest status when failures are
detected.
The following sysfs file is created:
* /sys/class/fpga_manager/<fpga>/status
Return status of fpga manager, including reconfiguration errors.
Signed-off-by: Wu Hao <redacted>
----
v3: add one line description for status
add status callback function to fpga_manager_ops
update fpga-mgr status if any failure or during initialization
s/INCOMPATIBLE_BS_ERR/INCOMPATIBLE_IMAGE_ERR/
---
Documentation/ABI/testing/sysfs-class-fpga-manager | 10 ++++++++
drivers/fpga/fpga-mgr.c | 28 ++++++++++++++++++++++
include/linux/fpga/fpga-mgr.h | 17 +++++++++++++
3 files changed, 55 insertions(+)
@@ -35,3 +35,13 @@ Description: Read fpga manager state as a string. * write complete = Doing post programming steps * write complete error = Error while doing post programming * operating = FPGA is programmed and operating++What: /sys/class/fpga_manager/<fpga>/status+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>+Description: Read fpga manager status as a string.+ If FPGA programming operation fails, it could be due to crc+ error or incompatible bitstream image. The intent of this+ interface is to provide more detailed information for FPGA+ programming errors to userspace.
On Mon, Dec 04, 2017 at 01:46:59PM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 9:15 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Nov 27, 2017 at 10:28:04AM +0000, David Laight wrote:
quoted
From: Wu Hao
quoted
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
Hi David
This patch adds a pcie device driver for Intel FPGA devices which implements
the DFL, e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards. They are pcie devices, and all have DFL implemented in
the MMIO space, so we would like to use one kernel driver to handle them.
With this full patchset, it just provides user the interfaces to configure
and access the FPGA accelerators on Intel DFL based FPGA devices. For sure,
users can develop and build their own logics via tools provided by Intel,
program them to accelerators on these Intel FPGA devices, and access them
for their workloads.
I don't see anything Intel specific here. This could all be named dfl-*
The maybe some device specific things, e.g Intel FPGA devices supported by this
driver always have FME DFL at the beginning on the BAR0 for PF device.
But I think this should be the right direction for better code reuse, it could
save efforts for other vendors who want to use DFL and follow the same way.
Thanks for the comments. I will rename this driver in the next version.
Hao
On Mon, Dec 04, 2017 at 02:26:14PM -0600, Alan Tull wrote:
On Wed, Nov 29, 2017 at 12:11 AM, Moritz Fischer [off-list ref] wrote:
quoted
Hi Hao,
On Mon, Nov 27, 2017 at 02:42:09PM +0800, Wu Hao wrote:
quoted
This patch adds region_id to fpga_image_info data structure, it
allows driver to pass region id information to fpga-mgr via
fpga_image_info for fpga reconfiguration function.
Signed-off-by: Wu Hao <redacted>
On Mon, Dec 04, 2017 at 01:55:37PM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
quoted
Add a document for Intel FPGA driver overview.
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Signed-off-by: Wu Hao <redacted>
----
v2: added FME fpga-mgr/bridge/region platform driver to driver organization.
updated open discussion per current implementation.
fixed some typos.
v3: use FPGA base region as container device instead of fpga-dev class.
split common enumeration code from pcie driver to functions exposed by
device feature list framework.
update FME performance reporting which supports both integrated (iperf/)
and discrete (dperf/) FPGA solutions.
---
Documentation/fpga/intel-fpga.txt | 261 ++++++++++++++++++++++++++++++++++++++
1 file changed, 261 insertions(+)
create mode 100644 Documentation/fpga/intel-fpga.txt
This doesn't look Intel specific to me. This could all be 'DFL FPGA Framework'
Sure, will rename this doc to dfl-fpga.txt in the next version as we plan to rename
the pcie driver to dfl-pci per your comments on the patch #8, there is no reason to
keep it in this doc as all drivers will be dfl-* in the next version. :)
quoted
+-------------------------------------------------------------------------------
+ Enno Luebbers [off-list ref]
+ Xiao Guangrong [off-list ref]
+ Wu Hao [off-list ref]
+
+The Intel FPGA driver provides interfaces for userspace applications to
+configure, enumerate, open, and access FPGA accelerators on platforms equipped
+with Intel(R) FPGA PCIe based solutions and enables system level management
+functions such as FPGA reconfiguration, power management, and virtualization.
+
+HW Architecture
+===============
+From the OS's point of view, the FPGA hardware appears as a regular PCIe device.
+The FPGA device memory is organized using a predefined data structure (Device
+Feature List). Features supported by the particular FPGA device are exposed
+through these data structures, as illustrated below:
+
+ +-------------------------------+ +-------------+
+ | PF | | VF |
+ +-------------------------------+ +-------------+
+ ^ ^ ^ ^
+ | | | |
++-----|------------|---------|--------------|-------+
+| | | | | |
+| +-----+ +-------+ +-------+ +-------+ |
+| | FME | | Port0 | | Port1 | | Port2 | |
+| +-----+ +-------+ +-------+ +-------+ |
+| ^ ^ ^ |
+| | | | |
+| +-------+ +------+ +-------+ |
+| | AFU | | AFU | | AFU | |
+| +-------+ +------+ +-------+ |
+| |
+| FPGA PCIe Device |
++---------------------------------------------------+
+
+The driver supports PCIe SR-IOV to create virtual functions (VFs) which can be
+used to assign individual accelerators to virtual machines.
+
+FME (FPGA Management Engine)
+============================
+The FPGA Management Engine performs power and thermal management, error
+reporting, reconfiguration, performance reporting for integrated and discrete
+solution, and other infrastructure functions. Each FPGA has one FME, which is
+always accessed through the physical function (PF).
+
+User-space applications can acquire exclusive access to the FME using open(),
+and release it using close().
+
+The following functions are exposed through ioctls:
+
+ Get driver API version (FPGA_GET_API_VERSION)
+ Check for extensions (FPGA_CHECK_EXTENSION)
+ Assign port to PF (FPGA_FME_PORT_ASSIGN)
+ Release port from PF (FPGA_FME_PORT_RELEASE)
+ Program bitstream (FPGA_FME_PORT_PR)
+
+More functions are exposed through sysfs
+(/sys/class/fpga_region/regionX/fpga-dfl-fme.n/):
I see that /sys/class/fpga/* has changed to /sys/class/fpga_region/*
now as requested (thanks!). It looks like it ended up being pretty
straightforward (so far, just diffing this doc with the previous v2).
Thanks for the suggestion on using fpga base region. :)
Hao
On Mon, Dec 04, 2017 at 02:55:42PM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
mgr->status isn't used anywhere except in status_show. So we don't
need to add status to the fpga_manager struct. Also don't need the
inline function to update it. Just read the status in status_show, if
that ops exists. If mops->status is NULL, return an error, as below.
This will simplify things and make sure that status_show gets status
that is not stale at all.
Hi Alan
You're right, this is much simpler. Will follow this to fix it in the
next version. Thanks!
Hao
From: Alan Tull <atull@kernel.org> Date: 2017-12-05 17:01:08
On Mon, Dec 4, 2017 at 9:33 PM, Wu Hao [off-list ref] wrote:
On Mon, Dec 04, 2017 at 01:46:59PM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 9:15 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Nov 27, 2017 at 10:28:04AM +0000, David Laight wrote:
quoted
From: Wu Hao
quoted
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
Hi David
This patch adds a pcie device driver for Intel FPGA devices which implements
the DFL, e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards. They are pcie devices, and all have DFL implemented in
the MMIO space, so we would like to use one kernel driver to handle them.
With this full patchset, it just provides user the interfaces to configure
and access the FPGA accelerators on Intel DFL based FPGA devices. For sure,
users can develop and build their own logics via tools provided by Intel,
program them to accelerators on these Intel FPGA devices, and access them
for their workloads.
I don't see anything Intel specific here. This could all be named dfl-*
The maybe some device specific things, e.g Intel FPGA devices supported by this
driver always have FME DFL at the beginning on the BAR0 for PF device.
But I think this should be the right direction for better code reuse, it could
save efforts for other vendors who want to use DFL and follow the same way.
Thanks for the comments. I will rename this driver in the next version.
Thanks!
Regarding file names, it seems like the files added to drivers/fpga
could be uniformly named dfl-*.[ch]. Some are fpga-dfl-*.[ch] while
other are currently dfl-*.[ch] currently.
Alan
On Tue, Dec 05, 2017 at 11:00:22AM -0600, Alan Tull wrote:
On Mon, Dec 4, 2017 at 9:33 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Dec 04, 2017 at 01:46:59PM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 9:15 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Nov 27, 2017 at 10:28:04AM +0000, David Laight wrote:
quoted
From: Wu Hao
quoted
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
Hi David
This patch adds a pcie device driver for Intel FPGA devices which implements
the DFL, e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards. They are pcie devices, and all have DFL implemented in
the MMIO space, so we would like to use one kernel driver to handle them.
With this full patchset, it just provides user the interfaces to configure
and access the FPGA accelerators on Intel DFL based FPGA devices. For sure,
users can develop and build their own logics via tools provided by Intel,
program them to accelerators on these Intel FPGA devices, and access them
for their workloads.
I don't see anything Intel specific here. This could all be named dfl-*
The maybe some device specific things, e.g Intel FPGA devices supported by this
driver always have FME DFL at the beginning on the BAR0 for PF device.
But I think this should be the right direction for better code reuse, it could
save efforts for other vendors who want to use DFL and follow the same way.
Thanks for the comments. I will rename this driver in the next version.
Thanks!
Regarding file names, it seems like the files added to drivers/fpga
could be uniformly named dfl-*.[ch]. Some are fpga-dfl-*.[ch] while
other are currently dfl-*.[ch] currently.
Sure, will have all related drivers files renamed to dfl-*.[ch].
Thanks
Hao
From: David Laight <hidden> Date: 2017-12-06 09:31:50
From: Alan Tull
Sent: 04 December 2017 19:47
On Mon, Nov 27, 2017 at 9:15 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Nov 27, 2017 at 10:28:04AM +0000, David Laight wrote:
quoted
From: Wu Hao
quoted
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
Hi David
This patch adds a pcie device driver for Intel FPGA devices which implements
the DFL, e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards. They are pcie devices, and all have DFL implemented in
the MMIO space, so we would like to use one kernel driver to handle them.
With this full patchset, it just provides user the interfaces to configure
and access the FPGA accelerators on Intel DFL based FPGA devices. For sure,
users can develop and build their own logics via tools provided by Intel,
program them to accelerators on these Intel FPGA devices, and access them
for their workloads.
I don't see anything Intel specific here. This could all be named dfl-*
Indeed, doesn't even seem to have to be implemented in an fpga.
It might also not be the only device that implements DFL.
You really need a name for your DFL acceleration implementation/interface.
We make a board that uses an Intel/Altera fpga as a PCIe device, won't look
anything like your one!
David
From: David Laight <hidden> Date: 2017-12-06 09:44:34
From: Wu Hao
Sent: 06 December 2017 05:30
...
quoted
Regarding file names, it seems like the files added to drivers/fpga
could be uniformly named dfl-*.[ch]. Some are fpga-dfl-*.[ch] while
other are currently dfl-*.[ch] currently.
They don't even want to do into a drivers/fgpa directory.
Maybe drivers/dfl or drivers/dfl/intel
David
From: Alan Tull <atull@kernel.org> Date: 2017-12-06 15:30:19
On Wed, Dec 6, 2017 at 3:44 AM, David Laight [off-list ref] wrote:
From: Wu Hao
quoted
Sent: 06 December 2017 05:30
...
quoted
quoted
Regarding file names, it seems like the files added to drivers/fpga
could be uniformly named dfl-*.[ch]. Some are fpga-dfl-*.[ch] while
other are currently dfl-*.[ch] currently.
They don't even want to do into a drivers/fgpa directory.
Maybe drivers/dfl or drivers/dfl/intel
It's plugged into the fpga framework in drivers/fpga. This patchset
also handles reprogramming the fpga, not just the dfl style
enumeration. But your points about this being not just for FPGA are
interesting to me. Do you have a use for this that isn't
FPGA-centric?
Alan
From: David Laight <hidden> Date: 2017-12-06 16:28:49
From: Alan Tull
Sent: 06 December 2017 15:30
On Wed, Dec 6, 2017 at 3:44 AM, David Laight [off-list ref] wrote:
quoted
From: Wu Hao
quoted
Sent: 06 December 2017 05:30
...
quoted
quoted
Regarding file names, it seems like the files added to drivers/fpga
could be uniformly named dfl-*.[ch]. Some are fpga-dfl-*.[ch] while
other are currently dfl-*.[ch] currently.
They don't even want to do into a drivers/fgpa directory.
Maybe drivers/dfl or drivers/dfl/intel
It's plugged into the fpga framework in drivers/fpga. This patchset
also handles reprogramming the fpga, not just the dfl style
enumeration. But your points about this being not just for FPGA are
interesting to me. Do you have a use for this that isn't
FPGA-centric?
That all just seems wrong to me.
If you've managed to invent some common code for reprogramming fpga
I'd have though it would be library functions.
The driver ought to sit somewhere related to its functionality.
Our fpga loads from a serial EEPROM, the image is about 6.5MB.
We can rewrite it from userspace by mmap()ing part of one of the BARs
to access some very locally written (by me) VHDL that does most of
the required bit-banging for 32it word accesses.
You really wouldn't want to load 6.5MB into kernel space!
We also had to solve the problem of 9 separate driver modules that
want to access different parts of the BARs.
I think we have 46 separate slaves in the fpgas BARs (most are in 1 BAR).
Some of these are common between different boards (or completely different
memory maps for the same board.
I can imagine some generic method of having a 'board' driver for a
specific PCI-id that knows the BAR offsets of various functions so that
other sub-drivers could be loaded to access those functions.
But that is some kind of pseudo-bus not fpga specific in any way.
David
From: Alan Tull <atull@kernel.org> Date: 2017-12-06 22:40:26
On Wed, Dec 6, 2017 at 10:28 AM, David Laight [off-list ref] wrote:
From: Alan Tull
quoted
Sent: 06 December 2017 15:30
On Wed, Dec 6, 2017 at 3:44 AM, David Laight [off-list ref] wrote:
quoted
From: Wu Hao
quoted
Sent: 06 December 2017 05:30
...
quoted
quoted
Regarding file names, it seems like the files added to drivers/fpga
could be uniformly named dfl-*.[ch]. Some are fpga-dfl-*.[ch] while
other are currently dfl-*.[ch] currently.
They don't even want to do into a drivers/fgpa directory.
Maybe drivers/dfl or drivers/dfl/intel
It's plugged into the fpga framework in drivers/fpga. This patchset
also handles reprogramming the fpga, not just the dfl style
enumeration. But your points about this being not just for FPGA are
interesting to me. Do you have a use for this that isn't
FPGA-centric?
That all just seems wrong to me.
If you've managed to invent some common code for reprogramming fpga
I'd have though it would be library functions.
Why don't you familiarize yourself with the fpga framework before
commenting on it? ;)
The driver ought to sit somewhere related to its functionality.
Our fpga loads from a serial EEPROM, the image is about 6.5MB.
We can rewrite it from userspace by mmap()ing part of one of the BARs
to access some very locally written (by me) VHDL that does most of
the required bit-banging for 32it word accesses.
The fpga framework is intended to handle cases where the it is desired
to reprogram the fpga a lot without having to reboot. It doesn't
sound like that is your use case.
You really wouldn't want to load 6.5MB into kernel space!
No, and there have been proposals, shot down so far regarding
streaming firmware images a page at a time.
We also had to solve the problem of 9 separate driver modules that
want to access different parts of the BARs.
I think we have 46 separate slaves in the fpgas BARs (most are in 1 BAR).
Some of these are common between different boards (or completely different
memory maps for the same board.
I can imagine some generic method of having a 'board' driver for a
specific PCI-id that knows the BAR offsets of various functions so that
other sub-drivers could be loaded to access those functions.
But that is some kind of pseudo-bus not fpga specific in any way.
David
On Wed, Dec 06, 2017 at 09:34:14AM +0000, David Laight wrote:
From: Wu Hao
quoted
Sent: 05 December 2017 03:34
...
quoted
quoted
I don't see anything Intel specific here. This could all be named dfl-*
The maybe some device specific things, e.g Intel FPGA devices supported by this
driver always have FME DFL at the beginning on the BAR0 for PF device.
Since when has that been a method for specifying what the card does?
You need to allocate a PCI-id for your DFL accelerator.
This driver only supports Intel FPGA devices (see PCI Device Ids table
in this patch) as mentioned above now, per my current understanding, if
other vendors follow the same hardware design on using DFL for their PCIe
based devices, then it's possible for them to fully reuse this code,
otherwise they need to develop new drivers for their own designs or extend
this driver in some ways (it depends on actual hardware implementation).
Thanks
Hao
From: Alan Tull <atull@kernel.org> Date: 2017-12-07 21:42:25
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
+/* enumerate feature devices under pci device */
+static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
+{
+ struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+ struct fpga_cdev *cdev;
+ struct fpga_enum_info *info;
+ resource_size_t start, len;
+ void __iomem *base;
+ int port_num, bar, i, ret = 0;
+ u32 offset;
+ u64 v;
+
+ /* allocate enumeration info via pci_dev */
+ info = fpga_enum_info_alloc(&pcidev->dev);
+ if (!info)
+ return -ENOMEM;
+
+ /* start to find Device Feature List from Bar 0 */
+ base = cci_pci_ioremap_bar(pcidev, 0);
+ if (!base) {
+ ret = -ENOMEM;
+ goto enum_info_free_exit;
+ }
+
+ /*
+ * PF device has FME and Ports/AFUs, and VF device only has 1 Port/AFU.
+ * check them and add related "Device Feature List" info for the next
+ * step enumeration.
+ */
+ if (feature_is_fme(base)) {
+ start = pci_resource_start(pcidev, 0);
+ len = pci_resource_len(pcidev, 0);
+
+ fpga_enum_info_add_dfl(info, start, len, base);
+
+ /*
+ * find more Device Feature Lists (e.g Ports) per information
+ * indicated by FME module.
+ */
+ v = readq(base + FME_HDR_CAP);
+ port_num = FIELD_GET(FME_CAP_NUM_PORTS, v);
+
+ WARN_ON(port_num > MAX_FPGA_PORT_NUM);
+
+ for (i = 0; i < port_num; i++) {
+ v = readq(base + FME_HDR_PORT_OFST(i));
+
+ /* skip ports which are not implemented. */
+ if (!(v & FME_PORT_OFST_IMP))
+ continue;
+
+ /*
+ * add Port's Device Feature List information for next
+ * step enumeration.
+ */
+ bar = FIELD_GET(FME_PORT_OFST_BAR_ID, v);
+ offset = FIELD_GET(FME_PORT_OFST_DFH_OFST, v);
+ base = cci_pci_ioremap_bar(pcidev, bar);
+ if (!base)
+ continue;
+
+ start = pci_resource_start(pcidev, bar) + offset;
+ len = pci_resource_len(pcidev, bar) - offset;
+
+ fpga_enum_info_add_dfl(info, start, len, base + offset);
+ }
+ } else if (feature_is_port(base)) {
+ start = pci_resource_start(pcidev, 0);
+ len = pci_resource_len(pcidev, 0);
+
+ fpga_enum_info_add_dfl(info, start, len, base);
+ } else {
+ ret = -ENODEV;
+ goto enum_info_free_exit;
+ }
+
+ /* start enumeration with prepared enumeration information */
+ cdev = fpga_enumerate_feature_devs(info);
Hi Hao,
I appreciate you separating the DFL enumeration code from this PCIe
module. This made the pcie part quite small. It should work for
embedded platforms just by adding a platform device whose function is
to find the DFL structures at some address and then call these same
fpga_enum_info_add_dfl adn fpga_enumerate_feature_devs functions.
Alan
On Thu, Dec 07, 2017 at 03:41:41PM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
quoted
+/* enumerate feature devices under pci device */
+static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
+{
+ struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+ struct fpga_cdev *cdev;
+ struct fpga_enum_info *info;
+ resource_size_t start, len;
+ void __iomem *base;
+ int port_num, bar, i, ret = 0;
+ u32 offset;
+ u64 v;
+
+ /* allocate enumeration info via pci_dev */
+ info = fpga_enum_info_alloc(&pcidev->dev);
+ if (!info)
+ return -ENOMEM;
+
+ /* start to find Device Feature List from Bar 0 */
+ base = cci_pci_ioremap_bar(pcidev, 0);
+ if (!base) {
+ ret = -ENOMEM;
+ goto enum_info_free_exit;
+ }
+
+ /*
+ * PF device has FME and Ports/AFUs, and VF device only has 1 Port/AFU.
+ * check them and add related "Device Feature List" info for the next
+ * step enumeration.
+ */
+ if (feature_is_fme(base)) {
+ start = pci_resource_start(pcidev, 0);
+ len = pci_resource_len(pcidev, 0);
+
+ fpga_enum_info_add_dfl(info, start, len, base);
+
+ /*
+ * find more Device Feature Lists (e.g Ports) per information
+ * indicated by FME module.
+ */
+ v = readq(base + FME_HDR_CAP);
+ port_num = FIELD_GET(FME_CAP_NUM_PORTS, v);
+
+ WARN_ON(port_num > MAX_FPGA_PORT_NUM);
+
+ for (i = 0; i < port_num; i++) {
+ v = readq(base + FME_HDR_PORT_OFST(i));
+
+ /* skip ports which are not implemented. */
+ if (!(v & FME_PORT_OFST_IMP))
+ continue;
+
+ /*
+ * add Port's Device Feature List information for next
+ * step enumeration.
+ */
+ bar = FIELD_GET(FME_PORT_OFST_BAR_ID, v);
+ offset = FIELD_GET(FME_PORT_OFST_DFH_OFST, v);
+ base = cci_pci_ioremap_bar(pcidev, bar);
+ if (!base)
+ continue;
+
+ start = pci_resource_start(pcidev, bar) + offset;
+ len = pci_resource_len(pcidev, bar) - offset;
+
+ fpga_enum_info_add_dfl(info, start, len, base + offset);
+ }
+ } else if (feature_is_port(base)) {
+ start = pci_resource_start(pcidev, 0);
+ len = pci_resource_len(pcidev, 0);
+
+ fpga_enum_info_add_dfl(info, start, len, base);
+ } else {
+ ret = -ENODEV;
+ goto enum_info_free_exit;
+ }
+
+ /* start enumeration with prepared enumeration information */
+ cdev = fpga_enumerate_feature_devs(info);
Hi Hao,
I appreciate you separating the DFL enumeration code from this PCIe
module. This made the pcie part quite small. It should work for
embedded platforms just by adding a platform device whose function is
to find the DFL structures at some address and then call these same
fpga_enum_info_add_dfl adn fpga_enumerate_feature_devs functions.
Yes, I think this is the right direction as you suggested. : )
Thanks
Hao
From: Alan Tull <atull@kernel.org> Date: 2017-12-12 18:18:57
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
quoted hunk
This patch adds status to fpga-manager data structure, to allow
driver to store full/partial reconfiguration errors and other
status information, and adds one status callback to fpga_manager_ops
to allow fpga_manager to collect latest status when failures are
detected.
The following sysfs file is created:
* /sys/class/fpga_manager/<fpga>/status
Return status of fpga manager, including reconfiguration errors.
Signed-off-by: Wu Hao <redacted>
----
v3: add one line description for status
add status callback function to fpga_manager_ops
update fpga-mgr status if any failure or during initialization
s/INCOMPATIBLE_BS_ERR/INCOMPATIBLE_IMAGE_ERR/
---
Documentation/ABI/testing/sysfs-class-fpga-manager | 10 ++++++++
drivers/fpga/fpga-mgr.c | 28 ++++++++++++++++++++++
include/linux/fpga/fpga-mgr.h | 17 +++++++++++++
3 files changed, 55 insertions(+)
@@ -35,3 +35,13 @@ Description: Read fpga manager state as a string. * write complete = Doing post programming steps * write complete error = Error while doing post programming * operating = FPGA is programmed and operating++What: /sys/class/fpga_manager/<fpga>/status+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read fpga manager status as a string.+ If FPGA programming operation fails, it could be due to crc+ error or incompatible bitstream image. The intent of this+ interface is to provide more detailed information for FPGA+ programming errors to userspace.
Hi Hao,
Please also add a list of status strings that can be given, such as
"reconfig operation error" I understand that this list may grow
somewhat in the future.
Alan
@@ -88,6 +88,7 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error preparing FPGA for writing\n");mgr->state=FPGA_MGR_STATE_WRITE_INIT_ERR;+fpga_mgr_update_status(mgr);returnret;}
@@ -148,6 +149,7 @@ static int fpga_mgr_write_complete(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error after writing image data to FPGA\n");mgr->state=FPGA_MGR_STATE_WRITE_COMPLETE_ERR;+fpga_mgr_update_status(mgr);returnret;}mgr->state=FPGA_MGR_STATE_OPERATING;
@@ -225,6 +227,7 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error while writing image data to FPGA\n");mgr->state=FPGA_MGR_STATE_WRITE_ERR;+fpga_mgr_update_status(mgr);returnret;}
On Tue, Dec 12, 2017 at 12:18:06PM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
quoted
This patch adds status to fpga-manager data structure, to allow
driver to store full/partial reconfiguration errors and other
status information, and adds one status callback to fpga_manager_ops
to allow fpga_manager to collect latest status when failures are
detected.
The following sysfs file is created:
* /sys/class/fpga_manager/<fpga>/status
Return status of fpga manager, including reconfiguration errors.
Signed-off-by: Wu Hao <redacted>
----
v3: add one line description for status
add status callback function to fpga_manager_ops
update fpga-mgr status if any failure or during initialization
s/INCOMPATIBLE_BS_ERR/INCOMPATIBLE_IMAGE_ERR/
---
Documentation/ABI/testing/sysfs-class-fpga-manager | 10 ++++++++
drivers/fpga/fpga-mgr.c | 28 ++++++++++++++++++++++
include/linux/fpga/fpga-mgr.h | 17 +++++++++++++
3 files changed, 55 insertions(+)
@@ -35,3 +35,13 @@ Description: Read fpga manager state as a string. * write complete = Doing post programming steps * write complete error = Error while doing post programming * operating = FPGA is programmed and operating++What: /sys/class/fpga_manager/<fpga>/status+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>+Description: Read fpga manager status as a string.+ If FPGA programming operation fails, it could be due to crc+ error or incompatible bitstream image. The intent of this+ interface is to provide more detailed information for FPGA+ programming errors to userspace.
Hi Hao,
Please also add a list of status strings that can be given, such as
"reconfig operation error" I understand that this list may grow
somewhat in the future.
@@ -88,6 +88,7 @@ static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error preparing FPGA for writing\n");mgr->state=FPGA_MGR_STATE_WRITE_INIT_ERR;+fpga_mgr_update_status(mgr);returnret;}
@@ -148,6 +149,7 @@ static int fpga_mgr_write_complete(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error after writing image data to FPGA\n");mgr->state=FPGA_MGR_STATE_WRITE_COMPLETE_ERR;+fpga_mgr_update_status(mgr);returnret;}mgr->state=FPGA_MGR_STATE_OPERATING;
@@ -225,6 +227,7 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,if(ret){dev_err(&mgr->dev,"Error while writing image data to FPGA\n");mgr->state=FPGA_MGR_STATE_WRITE_ERR;+fpga_mgr_update_status(mgr);returnret;}
I see that the port code is included as part of the enumeration code.
This is not very future-proofed, if a different port needs to be
supported.
The port is a FPGA fabric based bridge with expanded functionality,
right? So it is similar to the altera freeze bridge, but adds the
ability to reset the fabric and some other features are promised in
the future, IIUC. I still think that the port could be implemented in
the bridge driver .c file instead of being here as part of the
enumeration code. For that to happen, some APIs would need to be
added to the bridge framework and the FPGA region framework. Then the
reset can be requested through a new FPGA region API function.
The advantage of this is that if this patchset evolves and there is
some other v2 port driver needed, it can be a different driver if it
needs to be.
If the port reset is really a fabric reset, (correct me if I'm
remembering wrongly) then it would be helpful to call it a
fabric_reset. This would be the first bridge driver supporting fabric
reset. I think it won't be the last.
So what I'm proposing would be added/changed would be:
* move all the bridge code to fpga-dfl-fme-br.c
* add .fabric_reset to bridge ops
* add fpga_bridges_reset to fpga-bridge.c (a new function that goes
through a list of bridges and calls the reset ops if it exists,
ignores the bridges where it doesn't exist)
* add fpga_region_fabric_reset to fpga-region.c. This function gets
the region, gets the bridges, calls fpga_bridges_reset (can steal code
from fpga_region_program_fpga)
* the rest of the patchset can use fpga_region_fabric_reset instead of
fpga_port_reset
Alan
From: Alan Tull <atull@kernel.org> Date: 2017-12-20 22:32:01
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
+
+PORT
+====
+A port represents the interface between the static FPGA fabric (the "blue
+bitstream") and a partially reconfigurable region containing an AFU (the "green
+bitstream"). It controls the communication from SW to the accelerator and
+exposes features such as reset and debug.
Hi Hao,
If I remember correctly, reset means that the accelerator gets reset
and this is something that is desirable to do between jobs. I've
asked for some documentation about the port reset function, partly
because the idea of being able to reset hardware from userspace
somehow scares me. So please find a good logical place to explain
what a port reset does and how it is safe for userspace to request it
at some arbitrary time and how it won't crash the kernel. We
discussed this in v2, I grepped v3 for it, maybe I missed it, but I
don't see it in v3. My understanding is that disabling and reenabling
the port bridge causes the accelerator in its FPGA region to get
reset.
Alan
+
+A PCIe device may have several ports and each port can be released from PF by
+FPGA_FME_PORT_RELEASE ioctl on FME, and exposed through a VF via PCIe sriov
+sysfs interface.
+
I see that the port code is included as part of the enumeration code.
This is not very future-proofed, if a different port needs to be
supported.
The port is a FPGA fabric based bridge with expanded functionality,
right? So it is similar to the altera freeze bridge, but adds the
ability to reset the fabric and some other features are promised in
the future, IIUC. I still think that the port could be implemented in
the bridge driver .c file instead of being here as part of the
enumeration code. For that to happen, some APIs would need to be
added to the bridge framework and the FPGA region framework. Then the
reset can be requested through a new FPGA region API function.
The advantage of this is that if this patchset evolves and there is
some other v2 port driver needed, it can be a different driver if it
needs to be.
If the port reset is really a fabric reset,
Actually 'fabric reset' is probably not clear enough. It's resetting
the hardware in a partial reconfiguration region, not just resetting
the bridge. I'm trying to come up with a term that makes that clear
what is getting reset is the contents of the region.
(correct me if I'm
remembering wrongly) then it would be helpful to call it a
fabric_reset. This would be the first bridge driver supporting fabric
reset. I think it won't be the last.
So what I'm proposing would be added/changed would be:
* move all the bridge code to fpga-dfl-fme-br.c
* add .fabric_reset to bridge ops
* add fpga_bridges_reset to fpga-bridge.c (a new function that goes
through a list of bridges and calls the reset ops if it exists,
ignores the bridges where it doesn't exist)
* add fpga_region_fabric_reset to fpga-region.c. This function gets
the region, gets the bridges, calls fpga_bridges_reset (can steal code
from fpga_region_program_fpga)
* the rest of the patchset can use fpga_region_fabric_reset instead of
fpga_port_reset
Alan
On Wed, Dec 20, 2017 at 04:31:15PM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
quoted
+
+PORT
+====
+A port represents the interface between the static FPGA fabric (the "blue
+bitstream") and a partially reconfigurable region containing an AFU (the "green
+bitstream"). It controls the communication from SW to the accelerator and
+exposes features such as reset and debug.
Hi Hao,
If I remember correctly, reset means that the accelerator gets reset
and this is something that is desirable to do between jobs. I've
asked for some documentation about the port reset function, partly
because the idea of being able to reset hardware from userspace
somehow scares me. So please find a good logical place to explain
what a port reset does and how it is safe for userspace to request it
at some arbitrary time and how it won't crash the kernel. We
discussed this in v2, I grepped v3 for it, maybe I missed it, but I
don't see it in v3. My understanding is that disabling and reenabling
the port bridge causes the accelerator in its FPGA region to get
reset.
@@ -50,6 +53,20 @@ #define FPGA_CHECK_EXTENSION _IO(FPGA_MAGIC, FPGA_BASE + 1)+/* IOCTLs for AFU file descriptor */++/**+ * FPGA_PORT_RESET - _IO(FPGA_MAGIC, PORT_BASE + 0)+ *+ * Reset the FPGA Port and its AFU. No parameters are supported.+ * Userspace can do Port reset at any time, e.g during DMA or PR. But+ * it should never cause any system level issue, only functional failure+ * (e.g DMA or PR operation failure) and be recoverable from the failure.+ * Return: 0 on success, -errno of failure+ */++#define FPGA_PORT_RESET _IO(FPGA_MAGIC, PORT_BASE + 0)+
Thanks
Hao
Alan
quoted
+
+A PCIe device may have several ports and each port can be released from PF by
+FPGA_FME_PORT_RELEASE ioctl on FME, and exposed through a VF via PCIe sriov
+sysfs interface.
+
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
I see that the port code is included as part of the enumeration code.
This is not very future-proofed, if a different port needs to be
supported.
The port is a FPGA fabric based bridge with expanded functionality,
right? So it is similar to the altera freeze bridge, but adds the
ability to reset the fabric and some other features are promised in
the future, IIUC. I still think that the port could be implemented in
the bridge driver .c file instead of being here as part of the
enumeration code. For that to happen, some APIs would need to be
added to the bridge framework and the FPGA region framework. Then the
reset can be requested through a new FPGA region API function.
The advantage of this is that if this patchset evolves and there is
some other v2 port driver needed, it can be a different driver if it
needs to be.
If the port reset is really a fabric reset,
Actually 'fabric reset' is probably not clear enough. It's resetting
the hardware in a partial reconfiguration region, not just resetting
the bridge. I'm trying to come up with a term that makes that clear
what is getting reset is the contents of the region.
quoted
(correct me if I'm
remembering wrongly) then it would be helpful to call it a
fabric_reset. This would be the first bridge driver supporting fabric
reset. I think it won't be the last.
So what I'm proposing would be added/changed would be:
* move all the bridge code to fpga-dfl-fme-br.c
* add .fabric_reset to bridge ops
* add fpga_bridges_reset to fpga-bridge.c (a new function that goes
through a list of bridges and calls the reset ops if it exists,
ignores the bridges where it doesn't exist)
* add fpga_region_fabric_reset to fpga-region.c. This function gets
the region, gets the bridges, calls fpga_bridges_reset (can steal code
from fpga_region_program_fpga)
* the rest of the patchset can use fpga_region_fabric_reset instead of
fpga_port_reset
Hi Alan
Actually I think we can't move all the bridge code to fpga-dfl-fme-br.c as
this bridge (and region) is created by FME PR sub feature code, mainly for
PR function. But user may need the reset function when run some workload
on target Port/AFU, if consider virtualization case (SRIOV), there is only
Port/AFU in each VF, and no FME in VF (that means nobody creates the fpga
region/bridge/region). So it's need from port platform driver side as well.
The orignal idea that creates fpga-mgr/bridges/regions under FME, is that
even we turned all Ports/AFUs into VFs (user can not see port platform
device and the user interfaces exposed by port driver on PF), but user
still can use FME to do PR to those Ports/AFUs in turned into VFs (assigned
in different virtual machines).
I fully agree with you, that we should avoid feature specific code in the
common enumeration code and feature device framework if possible. I guess
I need some time to check and see if any other solutions (e.g export those
functions from port driver not DFL framework). Will back here once I have
some clear idea.:)
Thanks
Hao
I see that the port code is included as part of the enumeration code.
This is not very future-proofed, if a different port needs to be
supported.
The port is a FPGA fabric based bridge with expanded functionality,
right? So it is similar to the altera freeze bridge, but adds the
ability to reset the fabric and some other features are promised in
the future, IIUC. I still think that the port could be implemented in
the bridge driver .c file instead of being here as part of the
enumeration code. For that to happen, some APIs would need to be
added to the bridge framework and the FPGA region framework. Then the
reset can be requested through a new FPGA region API function.
The advantage of this is that if this patchset evolves and there is
some other v2 port driver needed, it can be a different driver if it
needs to be.
If the port reset is really a fabric reset,
Actually 'fabric reset' is probably not clear enough. It's resetting
the hardware in a partial reconfiguration region, not just resetting
the bridge. I'm trying to come up with a term that makes that clear
what is getting reset is the contents of the region.
quoted
(correct me if I'm
remembering wrongly) then it would be helpful to call it a
fabric_reset. This would be the first bridge driver supporting fabric
reset. I think it won't be the last.
So what I'm proposing would be added/changed would be:
* move all the bridge code to fpga-dfl-fme-br.c
* add .fabric_reset to bridge ops
* add fpga_bridges_reset to fpga-bridge.c (a new function that goes
through a list of bridges and calls the reset ops if it exists,
ignores the bridges where it doesn't exist)
* add fpga_region_fabric_reset to fpga-region.c. This function gets
the region, gets the bridges, calls fpga_bridges_reset (can steal code
from fpga_region_program_fpga)
* the rest of the patchset can use fpga_region_fabric_reset instead of
fpga_port_reset
Hi Alan
Actually I think we can't move all the bridge code to fpga-dfl-fme-br.c as
this bridge (and region) is created by FME PR sub feature code, mainly for
PR function. But user may need the reset function when run some workload
on target Port/AFU, if consider virtualization case (SRIOV), there is only
Port/AFU in each VF, and no FME in VF (that means nobody creates the fpga
region/bridge/region). So it's need from port platform driver side as well.
The orignal idea that creates fpga-mgr/bridges/regions under FME, is that
even we turned all Ports/AFUs into VFs (user can not see port platform
device and the user interfaces exposed by port driver on PF), but user
still can use FME to do PR to those Ports/AFUs in turned into VFs (assigned
in different virtual machines).
I fully agree with you, that we should avoid feature specific code in the
common enumeration code and feature device framework if possible. I guess
I need some time to check and see if any other solutions (e.g export those
functions from port driver not DFL framework). Will back here once I have
some clear idea.:)
Hi Alan
I checked further on this, it seems no good method to avoid feature_dev
specific code (e.g port/fme related code) in DFL framework, as it needs to
manage feature devices for virtualization cases. I tried that, make some
changes that the port reset code could be exported by the port platform
device instead, and fpga-dfl-fme-br.c depends on port platform device to
implement the bridge ops, but 1) it introduced more dependency between
these driver modules which seems not good. (ideally it's better that PR
could be done by FME module itself, no need to have some dependency on
other modules, e.g Port). 2) still have other port code (e.g fpga_port_id
which is useful for port management code in framework) can't be moved to
port platform driver module in the same method. As hardware is designed
this way, even we see separated device features in the DFL, but they have
a lot of dependency internally in different use cases (e.g PR, SRIOV and
etc).
Thanks
Hao
Thanks
Hao
quoted
quoted
Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Alan Tull <atull@kernel.org> Date: 2018-01-31 14:53:19
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
I'm adding my "Acked-by' below. When you post v4, please add it so
that we can keep track of what got acked.
Thanks,
Alan
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
This patch implements above 2 ioctls in FPGA Accelerated Function Unit
(AFU) driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Signed-off-by: Wu Hao <redacted>
From: Alan Tull <atull@kernel.org> Date: 2018-01-31 15:17:41
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
One fix below. Besides that, please add my ack.
This patch adds fpga bridge platform driver for FPGA Management Engine.
It implements the enable_set call back for fpga bridge.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Wu Hao <redacted>
From: Alan Tull <atull@kernel.org> Date: 2018-01-31 15:32:42
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
One fix again, otherwise please add my ack to subsequent versions.
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
Userspace application can use these ioctl interfaces to get the API info
and check if specific extension is supported or not in current driver.
This patch implements above 2 ioctls in FPGA Management Engine (FME)
driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Signed-off-by: Wu Hao <redacted>
From: Alan Tull <atull@kernel.org> Date: 2018-01-31 15:36:03
On Mon, Dec 4, 2017 at 9:36 PM, Wu Hao [off-list ref] wrote:
On Mon, Dec 04, 2017 at 02:26:14PM -0600, Alan Tull wrote:
quoted
On Wed, Nov 29, 2017 at 12:11 AM, Moritz Fischer [off-list ref] wrote:
quoted
Hi Hao,
On Mon, Nov 27, 2017 at 02:42:09PM +0800, Wu Hao wrote:
quoted
This patch adds region_id to fpga_image_info data structure, it
allows driver to pass region id information to fpga-mgr via
fpga_image_info for fpga reconfiguration function.
Signed-off-by: Wu Hao <redacted>
Acked-by: Moritz Fischer <redacted>
Acked-by: Alan Tull <redacted>
Hi Alan / Moritz
Thanks for the review. :)
Just to be clear, when you post the next version, keep the Acks that
have been added.
Thanks,
Alan
From: Alan Tull <atull@kernel.org> Date: 2018-01-31 20:47:12
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
Just fix the SPDX, otherwise it looks good.
This patch adds fpga region platform driver for FPGA Management Engine.
It register an fpga region with given fpga manager / bridge device.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Wu Hao <redacted>
From: Alan Tull <atull@kernel.org> Date: 2018-01-31 23:23:38
On Fri, Dec 22, 2017 at 2:45 AM, Wu Hao [off-list ref] wrote:
quoted
quoted
quoted
I see that the port code is included as part of the enumeration code.
This is not very future-proofed, if a different port needs to be
supported.
The port is a FPGA fabric based bridge with expanded functionality,
right? So it is similar to the altera freeze bridge, but adds the
ability to reset the fabric and some other features are promised in
the future, IIUC. I still think that the port could be implemented in
the bridge driver .c file instead of being here as part of the
enumeration code. For that to happen, some APIs would need to be
added to the bridge framework and the FPGA region framework. Then the
reset can be requested through a new FPGA region API function.
The advantage of this is that if this patchset evolves and there is
some other v2 port driver needed, it can be a different driver if it
needs to be.
If the port reset is really a fabric reset,
Actually 'fabric reset' is probably not clear enough. It's resetting
the hardware in a partial reconfiguration region, not just resetting
the bridge. I'm trying to come up with a term that makes that clear
what is getting reset is the contents of the region.
quoted
(correct me if I'm
remembering wrongly) then it would be helpful to call it a
fabric_reset. This would be the first bridge driver supporting fabric
reset. I think it won't be the last.
So what I'm proposing would be added/changed would be:
* move all the bridge code to fpga-dfl-fme-br.c
* add .fabric_reset to bridge ops
* add fpga_bridges_reset to fpga-bridge.c (a new function that goes
through a list of bridges and calls the reset ops if it exists,
ignores the bridges where it doesn't exist)
* add fpga_region_fabric_reset to fpga-region.c. This function gets
the region, gets the bridges, calls fpga_bridges_reset (can steal code
from fpga_region_program_fpga)
* the rest of the patchset can use fpga_region_fabric_reset instead of
fpga_port_reset
Hi Alan
Actually I think we can't move all the bridge code to fpga-dfl-fme-br.c as
this bridge (and region) is created by FME PR sub feature code, mainly for
PR function. But user may need the reset function when run some workload
on target Port/AFU, if consider virtualization case (SRIOV), there is only
Port/AFU in each VF, and no FME in VF (that means nobody creates the fpga
region/bridge/region). So it's need from port platform driver side as well.
The orignal idea that creates fpga-mgr/bridges/regions under FME, is that
even we turned all Ports/AFUs into VFs (user can not see port platform
device and the user interfaces exposed by port driver on PF), but user
still can use FME to do PR to those Ports/AFUs in turned into VFs (assigned
in different virtual machines).
I fully agree with you, that we should avoid feature specific code in the
common enumeration code and feature device framework if possible. I guess
I need some time to check and see if any other solutions (e.g export those
functions from port driver not DFL framework). Will back here once I have
some clear idea.:)
Hi Alan
I checked further on this, it seems no good method to avoid feature_dev
specific code (e.g port/fme related code) in DFL framework, as it needs to
manage feature devices for virtualization cases. I tried that, make some
changes that the port reset code could be exported by the port platform
device instead, and fpga-dfl-fme-br.c depends on port platform device to
implement the bridge ops, but 1) it introduced more dependency between
these driver modules which seems not good. (ideally it's better that PR
could be done by FME module itself, no need to have some dependency on
other modules, e.g Port). 2) still have other port code (e.g fpga_port_id
which is useful for port management code in framework) can't be moved to
port platform driver module in the same method. As hardware is designed
this way, even we see separated device features in the DFL, but they have
a lot of dependency internally in different use cases (e.g PR, SRIOV and
etc).
Hi Hao,
OK, well sounds like it's not feasible then. Thanks for looking into it.
Alan
Thanks
Hao
quoted
Thanks
Hao
quoted
quoted
Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Jan 31, 2018 at 09:35:20AM -0600, Alan Tull wrote:
On Mon, Dec 4, 2017 at 9:36 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Dec 04, 2017 at 02:26:14PM -0600, Alan Tull wrote:
quoted
On Wed, Nov 29, 2017 at 12:11 AM, Moritz Fischer [off-list ref] wrote:
quoted
Hi Hao,
On Mon, Nov 27, 2017 at 02:42:09PM +0800, Wu Hao wrote:
quoted
This patch adds region_id to fpga_image_info data structure, it
allows driver to pass region id information to fpga-mgr via
fpga_image_info for fpga reconfiguration function.
Signed-off-by: Wu Hao <redacted>
Acked-by: Moritz Fischer <mdf@kernel.org>
Acked-by: Alan Tull <atull@kernel.org>
Hi Alan / Moritz
Thanks for the review. :)
Just to be clear, when you post the next version, keep the Acks that
have been added.
Sure, I will keep the Acks in the next version.
Thanks
Hao
On Wed, Jan 31, 2018 at 09:31:59AM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
One fix again, otherwise please add my ack to subsequent versions.
Sure. Thanks for the review.
quoted
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
Userspace application can use these ioctl interfaces to get the API info
and check if specific extension is supported or not in current driver.
This patch implements above 2 ioctls in FPGA Management Engine (FME)
driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
On Wed, Jan 31, 2018 at 09:16:58AM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
One fix below. Besides that, please add my ack.
quoted
This patch adds fpga bridge platform driver for FPGA Management Engine.
It implements the enable_set call back for fpga bridge.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Wu Hao <redacted>
On Wed, Jan 31, 2018 at 08:52:36AM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
I'm adding my "Acked-by' below. When you post v4, please add it so
that we can keep track of what got acked.
Sure, thanks a lot for the code review. :)
Hao
Thanks,
Alan
quoted
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
This patch implements above 2 ioctls in FPGA Accelerated Function Unit
(AFU) driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
Acked-by: Alan Tull <atull@kernel.org>
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, Jan 31, 2018 at 02:46:28PM -0600, Alan Tull wrote:
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
Just fix the SPDX, otherwise it looks good.
Sure, will fix it. Thanks for the review. :)
Thanks
Hao
quoted
This patch adds fpga region platform driver for FPGA Management Engine.
It register an fpga region with given fpga manager / bridge device.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Wu Hao <redacted>
From: Moritz Fischer <mdf@kernel.org> Date: 2018-02-01 15:11:10
Hi Hao,
On Thu, Feb 01, 2018 at 01:15:43PM +0800, Wu Hao wrote:
On Wed, Jan 31, 2018 at 09:16:58AM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
One fix below. Besides that, please add my ack.
quoted
This patch adds fpga bridge platform driver for FPGA Management Engine.
It implements the enable_set call back for fpga bridge.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Wu Hao <redacted>
From: Moritz Fischer <mdf@kernel.org> Date: 2018-02-01 15:12:02
On Thu, Feb 01, 2018 at 01:11:07PM +0800, Wu Hao wrote:
On Wed, Jan 31, 2018 at 09:31:59AM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
One fix again, otherwise please add my ack to subsequent versions.
Sure. Thanks for the review.
quoted
quoted
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
Userspace application can use these ioctl interfaces to get the API info
and check if specific extension is supported or not in current driver.
This patch implements above 2 ioctls in FPGA Management Engine (FME)
driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
From: Moritz Fischer <mdf@kernel.org> Date: 2018-02-01 15:13:14
On Thu, Feb 01, 2018 at 01:16:25PM +0800, Wu Hao wrote:
On Wed, Jan 31, 2018 at 08:52:36AM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
I'm adding my "Acked-by' below. When you post v4, please add it so
that we can keep track of what got acked.
Sure, thanks a lot for the code review. :)
Hao
quoted
Thanks,
Alan
quoted
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
This patch implements above 2 ioctls in FPGA Accelerated Function Unit
(AFU) driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <redacted>
Signed-off-by: Wu Hao <redacted>
Acked-by: Alan Tull <atull@kernel.org>
Acked-by: Moritz Fischer <mdf@kernel.org>
quoted
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Moritz Fischer <mdf@kernel.org> Date: 2018-02-01 15:13:51
On Thu, Feb 01, 2018 at 01:23:45PM +0800, Wu Hao wrote:
On Wed, Jan 31, 2018 at 02:46:28PM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
Just fix the SPDX, otherwise it looks good.
Sure, will fix it. Thanks for the review. :)
Thanks
Hao
quoted
quoted
This patch adds fpga region platform driver for FPGA Management Engine.
It register an fpga region with given fpga manager / bridge device.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Wu Hao <redacted>
From: Alan Tull <atull@kernel.org> Date: 2018-02-01 22:00:04
On Tue, Dec 5, 2017 at 11:30 PM, Wu Hao [off-list ref] wrote:
On Tue, Dec 05, 2017 at 11:00:22AM -0600, Alan Tull wrote:
quoted
On Mon, Dec 4, 2017 at 9:33 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Dec 04, 2017 at 01:46:59PM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 9:15 PM, Wu Hao [off-list ref] wrote:
quoted
On Mon, Nov 27, 2017 at 10:28:04AM +0000, David Laight wrote:
quoted
From: Wu Hao
quoted
Sent: 27 November 2017 06:42
From: Zhang Yi <redacted>
The Intel FPGA device appears as a PCIe device on the system. This patch
implements the basic framework of the driver for Intel PCIe device which
is located between CPU and Accelerated Function Units (AFUs), and has
the Device Feature List (DFL) implemented in its MMIO space.
This ought to have a better name than 'Intel FPGA'.
An fpga can be used for all sorts of things, this looks like
a very specific architecture using a common VHDL environment to
allow certain types of user VHDL be accessed over PCIe.
Hi David
This patch adds a pcie device driver for Intel FPGA devices which implements
the DFL, e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards. They are pcie devices, and all have DFL implemented in
the MMIO space, so we would like to use one kernel driver to handle them.
With this full patchset, it just provides user the interfaces to configure
and access the FPGA accelerators on Intel DFL based FPGA devices. For sure,
users can develop and build their own logics via tools provided by Intel,
program them to accelerators on these Intel FPGA devices, and access them
for their workloads.
I don't see anything Intel specific here. This could all be named dfl-*
The maybe some device specific things, e.g Intel FPGA devices supported by this
driver always have FME DFL at the beginning on the BAR0 for PF device.
I'm thinking that another user could add their PCI id's and a static
FPGA image that has a DFL in the right place for this to work for
them.
quoted
quoted
But I think this should be the right direction for better code reuse, it could
save efforts for other vendors who want to use DFL and follow the same way.
I appreciate your understanding here.
quoted
quoted
Thanks for the comments. I will rename this driver in the next version.
Thanks!
Regarding file names, it seems like the files added to drivers/fpga
could be uniformly named dfl-*.[ch]. Some are fpga-dfl-*.[ch] while
other are currently dfl-*.[ch] currently.
Sure, will have all related drivers files renamed to dfl-*.[ch].
Actually, I'll reverse that a bit. The enumeration code, including
the pcie part is all sufficiently general to run on anything that has
a DFL struct located in the right place. But individual feature
drivers (currently only the fme-mgr) will be vendor specific and could
be named intel-*.
Alan
@@ -0,0 +1,8 @@+What: /sys/bus/platform/devices/fpga-dfl-fme-mgr.0/interface_id+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>+Description: Read-only. It returns interface id of partial reconfiguration+ hardware. Userspace could use this information to check if+ current hardware is compatible with given image before FPGA+ programming.
I'm a little confused by this. I can understand that the PR bitstream
has a dependency on the FPGA's static image, but I don't understand
the dependency of the bistream on the hardware that is used to program
the bitstream to the FPGA.
We have two dev_err's with the same "maximum PR timeout" so the user
would not be able to see at which point the timeout occurred.
I suggest to get rid of the word 'maximum' for both. This one could
be 'reset ack timeout" or something similar.
+ return -ETIMEDOUT;
+ }
+
+ pr_ctrl = readq(fme_pr + FME_PR_CTRL);
+ pr_ctrl &= ~FME_PR_CTRL_PR_RST;
+ writeq(pr_ctrl, fme_pr + FME_PR_CTRL);
+
+ dev_dbg(dev,
+ "waiting for PR resource in HW to be initialized and ready\n");
+
+ if (readq_poll_timeout(fme_pr + FME_PR_STS, pr_status,
+ (pr_status & FME_PR_STS_PR_STS) ==
+ FME_PR_STS_PR_STS_IDLE, 1, PR_WAIT_TIMEOUT)) {
+ dev_err(dev, "maximum PR timeout\n");
It looks like this is waiting for an entry in a queue and timing out
here. Could you add a some comments for pr_credit above and this
loop? Also a better error message, perhaps "PR credit timeout"?
@@ -130,7 +130,44 @@/* FME Partial Reconfiguration Sub Feature Register Set */#define FME_PR_DFH DFH-#define FME_PR_SIZE DFH_SIZE+#define FME_PR_CTRL 0x8+#define FME_PR_STS 0x10+#define FME_PR_DATA 0x18+#define FME_PR_ERR 0x20+#define FME_PR_INTFC_ID_H 0xA8+#define FME_PR_INTFC_ID_L 0xB0+#define FME_PR_SIZE 0xB8++/* FME PR Control Register Bitfield */+#define FME_PR_CTRL_PR_RST BIT(0) /* Reset PR engine */+#define FME_PR_CTRL_PR_RSTACK BIT(4) /* Ack for PR engine reset */+#define FME_PR_CTRL_PR_RGN_ID GENMASK_ULL(9, 7) /* PR Region ID */+#define FME_PR_CTRL_PR_START BIT(12) /* Start to request for PR service */+#define FME_PR_CTRL_PR_COMPLETE BIT(13) /* PR data push complete notification */++/* FME PR Status Register Bitfield */+/* Number of available entries in HW queue inside the PR engine. */+#define FME_PR_STS_PR_CREDIT GENMASK_ULL(8, 0)+#define FME_PR_STS_PR_STS BIT(16) /* PR operation status */+#define FME_PR_STS_PR_STS_IDLE 0+#define FME_PR_STS_PR_CTRLR_STS GENMASK_ULL(22, 20) /* Controller status */+#define FME_PR_STS_PR_HOST_STS GENMASK_ULL(27, 24) /* PR host status */++/* FME PR Data Register Bitfield */+/* PR data from the raw-binary file. */+#define FME_PR_DATA_PR_DATA_RAW GENMASK_ULL(32, 0)++/* FME PR Error Register */+/* Previous PR Operation errors detected. */+#define FME_PR_ERR_OPERATION_ERR BIT(0)+/* CRC error detected. */+#define FME_PR_ERR_CRC_ERR BIT(1)+/* Incompatible PR bitstream detected. */+#define FME_PR_ERR_INCOMPATIBLE_BS BIT(2)+/* PR data push protocol violated. */+#define FME_PR_ERR_PROTOCOL_ERR BIT(3)+/* PR data fifo overflow error detected */+#define FME_PR_ERR_FIFO_OVERFLOW BIT(4)/* FME HSSI Sub Feature Register Set */#define FME_HSSI_DFH DFH
I see fpga-dfl.h as enumeration code which is separate from any driver
implementation specifics other than what's required for the DFL
enumeration scheme. These PR engine #defines should move to a .h
that is dedicated to this specific PR hardware device. If someone else
adds a different PR device to the framework, their PR driver would
also have its own .h. Same for any other modules that aren't central
to DFL enumeration.
Thanks,
Alan
On Thu, Feb 01, 2018 at 04:13:09PM +0100, Moritz Fischer wrote:
On Thu, Feb 01, 2018 at 01:16:25PM +0800, Wu Hao wrote:
quoted
On Wed, Jan 31, 2018 at 08:52:36AM -0600, Alan Tull wrote:
quoted
On Mon, Nov 27, 2017 at 12:42 AM, Wu Hao [off-list ref] wrote:
Hi Hao,
I'm adding my "Acked-by' below. When you post v4, please add it so
that we can keep track of what got acked.
Sure, thanks a lot for the code review. :)
Hao
quoted
Thanks,
Alan
quoted
FPGA_GET_API_VERSION and FPGA_CHECK_EXTENSION ioctls are common ones which
need to be supported by all feature devices drivers including FME and AFU.
This patch implements above 2 ioctls in FPGA Accelerated Function Unit
(AFU) driver.
Signed-off-by: Tim Whisonant <redacted>
Signed-off-by: Enno Luebbers <redacted>
Signed-off-by: Shiva Rao <redacted>
Signed-off-by: Christopher Rauer <redacted>
Signed-off-by: Xiao Guangrong <guangrong.xiao-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Signed-off-by: Wu Hao <redacted>
Acked-by: Alan Tull <redacted>
Acked-by: Moritz Fischer <redacted>
quoted
quoted
--
To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
@@ -0,0 +1,8 @@+What: /sys/bus/platform/devices/fpga-dfl-fme-mgr.0/interface_id+Date: November 2017+KernelVersion: 4.15+Contact: Wu Hao <hao.wu@intel.com>+Description: Read-only. It returns interface id of partial reconfiguration+ hardware. Userspace could use this information to check if+ current hardware is compatible with given image before FPGA+ programming.
I'm a little confused by this. I can understand that the PR bitstream
has a dependency on the FPGA's static image, but I don't understand
the dependency of the bistream on the hardware that is used to program
the bitstream to the FPGA.
Sorry for the confusion, the interface_id is used to indicate the version of
the hardware for partial reconfiguration (it's part of the static image of
the FPGA device). Will improve the description on this.
We have two dev_err's with the same "maximum PR timeout" so the user
would not be able to see at which point the timeout occurred.
I suggest to get rid of the word 'maximum' for both. This one could
be 'reset ack timeout" or something similar.
Sure, will switch to a more specific message per your suggestion. Thanks.
quoted
+ return -ETIMEDOUT;
+ }
+
+ pr_ctrl = readq(fme_pr + FME_PR_CTRL);
+ pr_ctrl &= ~FME_PR_CTRL_PR_RST;
+ writeq(pr_ctrl, fme_pr + FME_PR_CTRL);
+
+ dev_dbg(dev,
+ "waiting for PR resource in HW to be initialized and ready\n");
+
+ if (readq_poll_timeout(fme_pr + FME_PR_STS, pr_status,
+ (pr_status & FME_PR_STS_PR_STS) ==
+ FME_PR_STS_PR_STS_IDLE, 1, PR_WAIT_TIMEOUT)) {
+ dev_err(dev, "maximum PR timeout\n");
It looks like this is waiting for an entry in a queue and timing out
here. Could you add a some comments for pr_credit above and this
loop? Also a better error message, perhaps "PR credit timeout"?
Driver needs to read the PR credit to know if it could push PR data
to hardware or not. I will add more description here on this PR credit,
and use "PR credit timeout" as error message.
@@ -130,7 +130,44 @@/* FME Partial Reconfiguration Sub Feature Register Set */#define FME_PR_DFH DFH-#define FME_PR_SIZE DFH_SIZE+#define FME_PR_CTRL 0x8+#define FME_PR_STS 0x10+#define FME_PR_DATA 0x18+#define FME_PR_ERR 0x20+#define FME_PR_INTFC_ID_H 0xA8+#define FME_PR_INTFC_ID_L 0xB0+#define FME_PR_SIZE 0xB8++/* FME PR Control Register Bitfield */+#define FME_PR_CTRL_PR_RST BIT(0) /* Reset PR engine */+#define FME_PR_CTRL_PR_RSTACK BIT(4) /* Ack for PR engine reset */+#define FME_PR_CTRL_PR_RGN_ID GENMASK_ULL(9, 7) /* PR Region ID */+#define FME_PR_CTRL_PR_START BIT(12) /* Start to request for PR service */+#define FME_PR_CTRL_PR_COMPLETE BIT(13) /* PR data push complete notification */++/* FME PR Status Register Bitfield */+/* Number of available entries in HW queue inside the PR engine. */+#define FME_PR_STS_PR_CREDIT GENMASK_ULL(8, 0)+#define FME_PR_STS_PR_STS BIT(16) /* PR operation status */+#define FME_PR_STS_PR_STS_IDLE 0+#define FME_PR_STS_PR_CTRLR_STS GENMASK_ULL(22, 20) /* Controller status */+#define FME_PR_STS_PR_HOST_STS GENMASK_ULL(27, 24) /* PR host status */++/* FME PR Data Register Bitfield */+/* PR data from the raw-binary file. */+#define FME_PR_DATA_PR_DATA_RAW GENMASK_ULL(32, 0)++/* FME PR Error Register */+/* Previous PR Operation errors detected. */+#define FME_PR_ERR_OPERATION_ERR BIT(0)+/* CRC error detected. */+#define FME_PR_ERR_CRC_ERR BIT(1)+/* Incompatible PR bitstream detected. */+#define FME_PR_ERR_INCOMPATIBLE_BS BIT(2)+/* PR data push protocol violated. */+#define FME_PR_ERR_PROTOCOL_ERR BIT(3)+/* PR data fifo overflow error detected */+#define FME_PR_ERR_FIFO_OVERFLOW BIT(4)/* FME HSSI Sub Feature Register Set */#define FME_HSSI_DFH DFH
I see fpga-dfl.h as enumeration code which is separate from any driver
implementation specifics other than what's required for the DFL
enumeration scheme. These PR engine #defines should move to a .h
that is dedicated to this specific PR hardware device. If someone else
adds a different PR device to the framework, their PR driver would
also have its own .h. Same for any other modules that aren't central
to DFL enumeration.
Sure, will move PR related register to a separated header file.
DFL enumeration related registers, will still be kept in fpga-dfl.h.
Thanks
Hao