2018-05-02 03:10:55

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 00/28] FPGA Device Feature List (DFL) Device Drivers

Hi All,

Here is v5 patch-series adding drivers for FPGA DFL devices.

This patch series provides a common framework to support FPGA Device Feature
List (DFL), and also feature dev drivers under this DFL framework to provide
interfaces for userspace applications to configure, enumerate, open, and
access FPGA accelerators on DFL based FPGA device and enables system level
management functions such as FPGA partial reconfiguration, power management
and virtualization.

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.

Note this patch series is only verified on DFL based Intel(R) FPGA PCIe
devices (e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
Acceleration Cards).

Patch 1: add a document for FPGA DFL framework driver overview, including
Device Feature List (DFL) introduction, the HW architecture, driver
organization, device enumeration 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 17)

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 18)

Patch 4-5: add compat_id support in fpga manager and region, this compat
id is used for compatibility check before further actions (e.g partial
reconfiguration). (Used by Patch 19 and 22)

Patch 6-11: 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. A global list is added to DFL framework to
manage port ops from different ports.

Patch 12-13: implement 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 14-17: 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 18-22: 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 23-28: 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 v4:
- Update the dfl.txt documentation, remove descriptions for the APIs and
features which are not implemented in this patch series.
- Add DFL_ / dfl_ prefix for APIs and data structure, to avoid directly
using DFL_ / fpga_xxx naming.
- Use "static region" and "PR bistream" instead of "blue bitstream" and
"green bistream" terminology in description to avoid misunderstanding.
- Fix building issues caused by BIT() on 64bit register definition and
missing correct header file for readq and writeq.
- Remove port specific code in DFL framework, and introduce port ops support
to resolve the dependency issue between FME driver module and Port driver
module. (more details in Patch 10).
- Add compat id to fpga manager, as in case some hardware implements the
compat id in fpga manager's register, not register belongs to fpga region,
and its value is shared by all related fpga regions.
- Pass mapped ioaddr to fme manager platform device from dfl-fme-pr via pdata.
- Move PR hardware register definition to dfl-fme-mgr.c.
- Fix other comments from Alan and Moritz, including description improvement,
coding style issue and etc.

Changes from v3:
- Fix SPDX license issue.
- Rename documentation to dfl.txt, add introduction for Device Feature List
(DFL) and re-organize the content.
- Rename to FPGA Device Feature List (DFL) drivers from Intel FPGA device
drivers for better reuse purposes. Unified driver and files to dfl-*.*
- Remove static feature_info table from common enumeration code, and switch
to use feature id for sub feature driver matching.
- Remove next_afu register checking for AFU from common enumeration code.
- Remove interface_id sysfs for dfl-fme-mgr and use per fpga-region
compat_id instead. (new patch 13, 15, 19).
- Add more comments for driver data structures and functions.
- Fix typos, issues in debug message/commit message and other places.

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 v4 0/4] fpga: change api, don't use drive data[1]

[1] https://marc.info/?l=linux-fpga&m=152356504900466&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 (22):
docs: fpga: add a document for FPGA Device Feature List (DFL)
Framework Overview
fpga: mgr: add region_id to fpga_image_info
fpga: mgr: add status for fpga-manager
fpga: mgr: add compat_id support
fpga: region: add compat_id support
fpga: add device feature list support
fpga: dfl: add chardev support for feature devices
fpga: dfl: add dfl_fpga_cdev_find_port
fpga: dfl: add dfl_fpga_port_ops support.
fpga: dfl: add dfl_fpga_check_port_id function.
fpga: dfl-pci: add enumeration for feature devices
fpga: dfl: fme: add DFL_FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls
support
fpga: dfl: add fpga manager platform driver for FME
fpga: dfl: fme-mgr: add compat_id support
fpga: dfl: add fpga bridge platform driver for FME
fpga: dfl: add fpga region platform driver for FME
fpga: dfl: fme-region: add support for compat_id
fpga: dfl: add FPGA Accelerated Function Unit driver basic framework
fpga: dfl: afu: add port ops support
fpga: dfl: afu: add header sub feature support
fpga: dfl: afu: add DFL_FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls
support
fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support

Xiao Guangrong (2):
fpga: dfl: add feature device infrastructure
fpga: dfl: afu: add afu sub feature support

Zhang Yi (1):
fpga: add FPGA DFL PCIe device driver

Documentation/ABI/testing/sysfs-class-fpga-manager | 24 +
Documentation/ABI/testing/sysfs-class-fpga-region | 9 +
Documentation/ABI/testing/sysfs-platform-dfl-fme | 23 +
Documentation/ABI/testing/sysfs-platform-dfl-port | 16 +
Documentation/fpga/dfl.txt | 285 ++++++
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/fpga/Kconfig | 68 ++
drivers/fpga/Makefile | 14 +
drivers/fpga/dfl-afu-dma-region.c | 463 +++++++++
drivers/fpga/dfl-afu-main.c | 636 +++++++++++++
drivers/fpga/dfl-afu-region.c | 166 ++++
drivers/fpga/dfl-afu.h | 100 ++
drivers/fpga/dfl-fme-br.c | 114 +++
drivers/fpga/dfl-fme-main.c | 279 ++++++
drivers/fpga/dfl-fme-mgr.c | 349 +++++++
drivers/fpga/dfl-fme-pr.c | 474 ++++++++++
drivers/fpga/dfl-fme-pr.h | 84 ++
drivers/fpga/dfl-fme-region.c | 89 ++
drivers/fpga/dfl-fme.h | 38 +
drivers/fpga/dfl-pci.c | 310 ++++++
drivers/fpga/dfl.c | 1000 ++++++++++++++++++++
drivers/fpga/dfl.h | 416 ++++++++
drivers/fpga/fpga-mgr.c | 28 +
drivers/fpga/fpga-region.c | 22 +
include/linux/fpga/fpga-mgr.h | 24 +
include/linux/fpga/fpga-region.h | 2 +
include/uapi/linux/fpga-dfl.h | 178 ++++
27 files changed, 5212 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-region
create mode 100644 Documentation/ABI/testing/sysfs-platform-dfl-fme
create mode 100644 Documentation/ABI/testing/sysfs-platform-dfl-port
create mode 100644 Documentation/fpga/dfl.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-br.c
create mode 100644 drivers/fpga/dfl-fme-main.c
create mode 100644 drivers/fpga/dfl-fme-mgr.c
create mode 100644 drivers/fpga/dfl-fme-pr.c
create mode 100644 drivers/fpga/dfl-fme-pr.h
create mode 100644 drivers/fpga/dfl-fme-region.c
create mode 100644 drivers/fpga/dfl-fme.h
create mode 100644 drivers/fpga/dfl-pci.c
create mode 100644 drivers/fpga/dfl.c
create mode 100644 drivers/fpga/dfl.h
create mode 100644 include/uapi/linux/fpga-dfl.h

--
1.8.3.1



2018-05-02 03:02:38

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 09/28] fpga: dfl: add feature device infrastructure

From: Xiao Guangrong <[email protected]>

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 dfl_feature_dev_use_begin/end which are used to ensure
exclusive usage of the feature device file.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Kang Luwei <[email protected]>
Signed-off-by: Zhang Yi <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
v2: rebased
v3: use const for feature_ops.
replace pci related function.
v4: rebase and add more comments in code.
v5: remove useless WARN_ON().
reorder declarations in functions per suggestion from Moritz.
add "dfl_" prefix to functions and data structure.
---
drivers/fpga/dfl.c | 57 +++++++++++++++++++++++++++++++++++++
drivers/fpga/dfl.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index 1e06efb..c4c47d6 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -74,6 +74,63 @@ static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
return DFL_ID_MAX;
}

+void dfl_fpga_dev_feature_uinit(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_feature *feature;
+
+ dfl_fpga_dev_for_each_feature(pdata, feature)
+ if (feature->ops) {
+ feature->ops->uinit(pdev, feature);
+ feature->ops = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit);
+
+static int dfl_feature_instance_init(struct platform_device *pdev,
+ struct dfl_feature_platform_data *pdata,
+ struct dfl_feature *feature,
+ struct dfl_feature_driver *drv)
+{
+ int ret;
+
+ ret = drv->ops->init(pdev, feature);
+ if (ret)
+ return ret;
+
+ feature->ops = drv->ops;
+
+ return ret;
+}
+
+int dfl_fpga_dev_feature_init(struct platform_device *pdev,
+ struct dfl_feature_driver *feature_drvs)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_feature_driver *drv = feature_drvs;
+ struct dfl_feature *feature;
+ int ret;
+
+ while (drv->ops) {
+ dfl_fpga_dev_for_each_feature(pdata, feature) {
+ /* match feature and drv using id */
+ if (feature->id == drv->id) {
+ ret = dfl_feature_instance_init(pdev, pdata,
+ feature, drv);
+ if (ret)
+ goto exit;
+ }
+ }
+ drv++;
+ }
+
+ return 0;
+exit:
+ dfl_fpga_dev_feature_uinit(pdev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_init);
+
struct dfl_chardev_info {
const char *name;
dev_t devt;
diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
index 2b6aaef..27f7a74 100644
--- a/drivers/fpga/dfl.h
+++ b/drivers/fpga/dfl.h
@@ -132,6 +132,17 @@
#define PORT_CTRL_SFTRST_ACK BIT_ULL(4) /* HW ack for reset */

/**
+ * struct dfl_feature_driver - sub feature's driver
+ *
+ * @id: sub feature id.
+ * @ops: ops of this sub feature.
+ */
+struct dfl_feature_driver {
+ u64 id;
+ const struct dfl_feature_ops *ops;
+};
+
+/**
* struct dfl_feature - sub feature of the feature devices
*
* @id: sub feature id.
@@ -139,13 +150,17 @@
* this index is used to find its mmio resource from the
* feature dev (platform device)'s reources.
* @ioaddr: mapped mmio resource address.
+ * @ops: ops of this sub feature.
*/
struct dfl_feature {
u64 id;
int resource_index;
void __iomem *ioaddr;
+ const struct dfl_feature_ops *ops;
};

+#define DEV_STATUS_IN_USE 0
+
/**
* struct dfl_feature_platform_data - platform data for feature devices
*
@@ -156,6 +171,8 @@ struct dfl_feature {
* @dfl_cdev: ptr to container device.
* @disable_count: count for port disable.
* @num: number for sub features.
+ * @dev_status: dev status (e.g DEV_STATUS_IN_USE).
+ * @private: ptr to feature dev private data.
* @features: sub features of this feature dev.
*/
struct dfl_feature_platform_data {
@@ -165,11 +182,49 @@ struct dfl_feature_platform_data {
struct platform_device *dev;
struct dfl_fpga_cdev *dfl_cdev;
unsigned int disable_count;
-
+ unsigned long dev_status;
+ void *private;
int num;
struct dfl_feature features[0];
};

+static inline
+int dfl_feature_dev_use_begin(struct dfl_feature_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;
+
+ return 0;
+}
+
+static inline
+void dfl_feature_dev_use_end(struct dfl_feature_platform_data *pdata)
+{
+ clear_bit_unlock(DEV_STATUS_IN_USE, &pdata->dev_status);
+}
+
+static inline
+void dfl_fpga_pdata_set_private(struct dfl_feature_platform_data *pdata,
+ void *private)
+{
+ pdata->private = private;
+}
+
+static inline
+void *dfl_fpga_pdata_get_private(struct dfl_feature_platform_data *pdata)
+{
+ return pdata->private;
+}
+
+struct dfl_feature_ops {
+ int (*init)(struct platform_device *pdev, struct dfl_feature *feature);
+ void (*uinit)(struct platform_device *pdev,
+ struct dfl_feature *feature);
+ long (*ioctl)(struct platform_device *pdev, struct dfl_feature *feature,
+ unsigned int cmd, unsigned long arg);
+};
+
#define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
#define DFL_FPGA_FEATURE_DEV_PORT "dfl-port"

@@ -179,6 +234,10 @@ static inline int dfl_feature_platform_data_size(const int num)
num * sizeof(struct dfl_feature);
}

+void dfl_fpga_dev_feature_uinit(struct platform_device *pdev);
+int dfl_fpga_dev_feature_init(struct platform_device *pdev,
+ struct dfl_feature_driver *feature_drvs);
+
enum dfl_fpga_devt_type {
DFL_FPGA_DEVT_FME,
DFL_FPGA_DEVT_PORT,
@@ -190,6 +249,16 @@ int dfl_fpga_register_dev_ops(struct platform_device *pdev,
struct module *owner);
void dfl_fpga_unregister_dev_ops(struct platform_device *pdev);

+static inline
+struct platform_device *dfl_fpga_inode_to_feature_dev(struct inode *inode)
+{
+ struct dfl_feature_platform_data *pdata;
+
+ pdata = container_of(inode->i_cdev, struct dfl_feature_platform_data,
+ cdev);
+ return pdata->dev;
+}
+
#define dfl_fpga_dev_for_each_feature(pdata, feature) \
for ((feature) = (pdata)->features; \
(feature) < (pdata)->features + (pdata)->num; (feature)++)
@@ -219,6 +288,17 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u64 id)
return NULL;
}

+static inline bool is_dfl_feature_present(struct device *dev, u64 id)
+{
+ return !!dfl_get_feature_ioaddr_by_id(dev, id);
+}
+
+static inline
+struct device *dfl_fpga_pdata_to_parent(struct dfl_feature_platform_data *pdata)
+{
+ return pdata->dev->dev.parent->parent;
+}
+
static inline bool dfl_feature_is_fme(void __iomem *base)
{
u64 v = readq(base + DFH);
--
1.8.3.1


2018-05-02 03:03:24

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 18/28] fpga: dfl: add fpga manager platform driver for FME

This patch adds fpga manager driver for FPGA Management Engine (FME). It
implements fpga_manager_ops for FPGA Partial Reconfiguration function.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Kang Luwei <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
v3: rename driver to dfl-fpga-fme-mgr
implemented status callback for fpga manager
rebased due to fpga api changes
v4: rename to dfl-fme-mgr, and fix SPDX license issue
add pr_credit comments and improve dev_err message
remove interface_id sysfs interface
include dfl-fme-pr.h instead of dfl.h
v5: move related register definition from dfl-fme-pr.h to dfl-fme-mgr.c
use mapped ioaddr passed via pdata for register access.
rebase due to fpga manager API changes.
include header file for readq/writeq.
---
drivers/fpga/Kconfig | 6 +
drivers/fpga/Makefile | 1 +
drivers/fpga/dfl-fme-mgr.c | 334 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 341 insertions(+)
create mode 100644 drivers/fpga/dfl-fme-mgr.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 103d5e2..89f76e8 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -150,6 +150,12 @@ config FPGA_DFL_FME
FPGA platform level management features. There shall be 1 FME
per DFL based FPGA device.

+config FPGA_DFL_FME_MGR
+ tristate "FPGA DFL FME Manager Driver"
+ depends on FPGA_DFL_FME
+ help
+ Say Y to enable FPGA Manager driver for FPGA Management Engine.
+
config FPGA_DFL_PCI
tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
depends on PCI && FPGA_DFL
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 3c44fc9..f82814a 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
# FPGA Device Feature List Support
obj-$(CONFIG_FPGA_DFL) += dfl.o
obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
+obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o

dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o

diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
new file mode 100644
index 0000000..1c5bc5a
--- /dev/null
+++ b/drivers/fpga/dfl-fme-mgr.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FPGA Manager Driver for FPGA Management Engine (FME)
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Wu Hao <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Christopher Rauer <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/fpga/fpga-mgr.h>
+
+#include "dfl-fme-pr.h"
+
+/* FME Partial Reconfiguration Sub Feature Register Set */
+#define FME_PR_DFH 0x0
+#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
+
+/* FME PR Control Register Bitfield */
+#define FME_PR_CTRL_PR_RST BIT_ULL(0) /* Reset PR engine */
+#define FME_PR_CTRL_PR_RSTACK BIT_ULL(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_ULL(12) /* Start to request PR service */
+#define FME_PR_CTRL_PR_COMPLETE BIT_ULL(13) /* PR data push completion */
+
+/* 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_ULL(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 */
+/* PR Operation errors detected. */
+#define FME_PR_ERR_OPERATION_ERR BIT_ULL(0)
+/* CRC error detected. */
+#define FME_PR_ERR_CRC_ERR BIT_ULL(1)
+/* Incompatible PR bitstream detected. */
+#define FME_PR_ERR_INCOMPATIBLE_BS BIT_ULL(2)
+/* PR data push protocol violated. */
+#define FME_PR_ERR_PROTOCOL_ERR BIT_ULL(3)
+/* PR data fifo overflow error detected */
+#define FME_PR_ERR_FIFO_OVERFLOW BIT_ULL(4)
+
+#define PR_WAIT_TIMEOUT 8000000
+#define PR_HOST_STATUS_IDLE 0
+
+struct fme_mgr_priv {
+ void __iomem *ioaddr;
+ u64 pr_error;
+};
+
+static u64 pr_error_to_mgr_status(u64 err)
+{
+ u64 status = 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;
+
+ return status;
+}
+
+static u64 fme_mgr_pr_error_handle(void __iomem *fme_pr)
+{
+ u64 pr_status, pr_error;
+
+ pr_status = readq(fme_pr + FME_PR_STS);
+ if (!(pr_status & FME_PR_STS_PR_STS))
+ return 0;
+
+ pr_error = readq(fme_pr + FME_PR_ERR);
+ writeq(pr_error, fme_pr + FME_PR_ERR);
+
+ return pr_error;
+}
+
+static int fme_mgr_write_init(struct fpga_manager *mgr,
+ struct fpga_image_info *info,
+ const char *buf, size_t count)
+{
+ struct device *dev = &mgr->dev;
+ struct fme_mgr_priv *priv = mgr->priv;
+ void __iomem *fme_pr = priv->ioaddr;
+ u64 pr_ctrl, pr_status;
+
+ if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ dev_err(dev, "only supports 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, "PR Reset ACK 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, "PR Status 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",
+ (unsigned long long)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);
+
+ return 0;
+}
+
+static int fme_mgr_write(struct fpga_manager *mgr,
+ const char *buf, size_t count)
+{
+ struct device *dev = &mgr->dev;
+ struct fme_mgr_priv *priv = mgr->priv;
+ void __iomem *fme_pr = priv->ioaddr;
+ u64 pr_ctrl, pr_status, pr_data;
+ int delay = 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");
+
+ /*
+ * driver can push data to PR hardware using PR_DATA register once HW
+ * has enough pr_credit (> 1), pr_credit reduces one for every 32bit
+ * pr data write to PR_DATA register. If pr_credit <= 1, driver needs
+ * to wait for enough pr_credit from hardware by polling.
+ */
+ 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, "PR_CREDIT timeout\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) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ 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++;
+ }
+
+ return 0;
+}
+
+static int fme_mgr_write_complete(struct fpga_manager *mgr,
+ struct fpga_image_info *info)
+{
+ struct device *dev = &mgr->dev;
+ struct fme_mgr_priv *priv = mgr->priv;
+ void __iomem *fme_pr = priv->ioaddr;
+ u64 pr_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, "PR Completion ACK timeout.\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",
+ (unsigned long long)priv->pr_error);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "PR done successfully\n");
+
+ return 0;
+}
+
+static enum fpga_mgr_states fme_mgr_state(struct fpga_manager *mgr)
+{
+ return FPGA_MGR_STATE_UNKNOWN;
+}
+
+static u64 fme_mgr_status(struct fpga_manager *mgr)
+{
+ struct fme_mgr_priv *priv = mgr->priv;
+
+ return pr_error_to_mgr_status(priv->pr_error);
+}
+
+static const struct fpga_manager_ops fme_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,
+};
+
+static int fme_mgr_probe(struct platform_device *pdev)
+{
+ struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ struct fme_mgr_priv *priv;
+ struct fpga_manager *mgr;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (pdata->ioaddr)
+ priv->ioaddr = pdata->ioaddr;
+
+ if (!priv->ioaddr) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->ioaddr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->ioaddr))
+ return PTR_ERR(priv->ioaddr);
+ }
+
+ mgr = fpga_mgr_create(dev, "DFL FME FPGA Manager",
+ &fme_mgr_ops, priv);
+ if (!mgr)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mgr);
+
+ ret = fpga_mgr_register(mgr);
+ if (ret)
+ fpga_mgr_free(mgr);
+
+ return ret;
+}
+
+static int fme_mgr_remove(struct platform_device *pdev)
+{
+ struct fpga_manager *mgr = platform_get_drvdata(pdev);
+
+ fpga_mgr_unregister(mgr);
+
+ return 0;
+}
+
+static struct platform_driver fme_mgr_driver = {
+ .driver = {
+ .name = DFL_FPGA_FME_MGR,
+ },
+ .probe = fme_mgr_probe,
+ .remove = fme_mgr_remove,
+};
+
+module_platform_driver(fme_mgr_driver);
+
+MODULE_DESCRIPTION("FPGA Manager for DFL FPGA Management Engine");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dfl-fme-mgr");
--
1.8.3.1


2018-05-02 03:03:40

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 20/28] fpga: dfl: add fpga bridge platform driver for FME

This patch adds fpga bridge platform driver for FPGA Management Engine.
It implements the enable_set callback for fpga bridge.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
---
v3: rename driver to fpga-dfl-fme-br
remove useless dev_dbg in probe function.
rebased due to fpga api change.
v4: rename to dfl-fme-br and fix SPDX license issue
include dfl-fme-pr.h instead of dfl-fme.h
add Acked-by from Alan and Moritz
v5: rebase due to API changes.
defer port and its ops finding when really need.
---
drivers/fpga/Kconfig | 6 +++
drivers/fpga/Makefile | 1 +
drivers/fpga/dfl-fme-br.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 121 insertions(+)
create mode 100644 drivers/fpga/dfl-fme-br.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 89f76e8..a8f939a 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -156,6 +156,12 @@ config FPGA_DFL_FME_MGR
help
Say Y to enable FPGA Manager driver for FPGA Management Engine.

+config FPGA_DFL_FME_BRIDGE
+ tristate "FPGA DFL FME Bridge Driver"
+ depends on FPGA_DFL_FME
+ help
+ Say Y to enable FPGA Bridge driver for FPGA Management Engine.
+
config FPGA_DFL_PCI
tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
depends on PCI && FPGA_DFL
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index f82814a..75096e9 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
obj-$(CONFIG_FPGA_DFL) += dfl.o
obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
+obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o

dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o

diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
new file mode 100644
index 0000000..5c51b08
--- /dev/null
+++ b/drivers/fpga/dfl-fme-br.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FPGA Bridge Driver for FPGA Management Engine (FME)
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Wu Hao <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/fpga/fpga-bridge.h>
+
+#include "dfl.h"
+#include "dfl-fme-pr.h"
+
+struct fme_br_priv {
+ struct dfl_fme_br_pdata *pdata;
+ struct dfl_fpga_port_ops *port_ops;
+ struct platform_device *port_pdev;
+};
+
+static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
+{
+ struct fme_br_priv *priv = bridge->priv;
+ struct platform_device *port_pdev;
+ struct dfl_fpga_port_ops *ops;
+
+ if (!priv->port_pdev) {
+ port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
+ &priv->pdata->port_id,
+ dfl_fpga_check_port_id);
+ if (!port_pdev)
+ return -ENODEV;
+
+ priv->port_pdev = port_pdev;
+ }
+
+ if (priv->port_pdev && !priv->port_ops) {
+ ops = dfl_fpga_get_port_ops(priv->port_pdev);
+ if (!ops || !ops->enable_set)
+ return -ENOENT;
+
+ priv->port_ops = ops;
+ }
+
+ return priv->port_ops->enable_set(priv->port_pdev, enable);
+}
+
+static const struct fpga_bridge_ops fme_bridge_ops = {
+ .enable_set = fme_bridge_enable_set,
+};
+
+static int fme_br_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fme_br_priv *priv;
+ struct fpga_bridge *br;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->pdata = dev_get_platdata(dev);
+
+ br = fpga_bridge_create(dev, "DFL FPGA FME Bridge",
+ &fme_bridge_ops, priv);
+ if (!br)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, br);
+
+ ret = fpga_bridge_register(br);
+ if (ret)
+ fpga_bridge_free(br);
+
+ return ret;
+}
+
+static int fme_br_remove(struct platform_device *pdev)
+{
+ struct fpga_bridge *br = platform_get_drvdata(pdev);
+ struct fme_br_priv *priv = br->priv;
+
+ fpga_bridge_unregister(br);
+
+ if (priv->port_pdev)
+ put_device(&priv->port_pdev->dev);
+ if (priv->port_ops)
+ dfl_fpga_put_port_ops(priv->port_ops);
+
+ return 0;
+}
+
+static struct platform_driver fme_br_driver = {
+ .driver = {
+ .name = DFL_FPGA_FME_BRIDGE,
+ },
+ .probe = fme_br_probe,
+ .remove = fme_br_remove,
+};
+
+module_platform_driver(fme_br_driver);
+
+MODULE_DESCRIPTION("FPGA Bridge for DFL FPGA Management Engine");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dfl-fme-bridge");
--
1.8.3.1


2018-05-02 03:03:43

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 05/28] fpga: region: add compat_id support

This patch introduces a compat_id pointer member and sysfs interface
for each fpga region, similar as compat_id for fpga manager, it allows
applications to read the per region compat_id for compatibility
checking before other actions on this fpga-region (e.g PR).

Signed-off-by: Wu Hao <[email protected]>
---
v5: use pointer for compat_id as it's optional to implement.
---
Documentation/ABI/testing/sysfs-class-fpga-region | 9 +++++++++
drivers/fpga/fpga-region.c | 22 ++++++++++++++++++++++
include/linux/fpga/fpga-region.h | 2 ++
3 files changed, 33 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-region

diff --git a/Documentation/ABI/testing/sysfs-class-fpga-region b/Documentation/ABI/testing/sysfs-class-fpga-region
new file mode 100644
index 0000000..9618f74
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-fpga-region
@@ -0,0 +1,9 @@
+What: /sys/class/fpga_region/<region>/compat_id
+Date: May 2018
+KernelVersion: 4.17
+Contact: Wu Hao <[email protected]>
+Description: FPGA region id for compatibility check, e.g compatibility
+ of the FPGA reconfiguration hardware and image. This value
+ is defined or calculated by the layer that is creating the
+ FPGA region. This interface returns the compat_id value or
+ just error code -ENOENT in case compat_id is not used.
diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
index d6a9dd2..6929178 100644
--- a/drivers/fpga/fpga-region.c
+++ b/drivers/fpga/fpga-region.c
@@ -162,6 +162,27 @@ int fpga_region_program_fpga(struct fpga_region *region)
}
EXPORT_SYMBOL_GPL(fpga_region_program_fpga);

+static ssize_t compat_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_region *region = to_fpga_region(dev);
+
+ if (!region->compat_id)
+ return -ENOENT;
+
+ return sprintf(buf, "%016llx%016llx\n",
+ (unsigned long long)region->compat_id->id_h,
+ (unsigned long long)region->compat_id->id_l);
+}
+
+static DEVICE_ATTR_RO(compat_id);
+
+static struct attribute *fpga_region_attrs[] = {
+ &dev_attr_compat_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(fpga_region);
+
/**
* fpga_region_create - alloc and init a struct fpga_region
* @dev: device parent
@@ -262,6 +283,7 @@ static int __init fpga_region_init(void)
if (IS_ERR(fpga_region_class))
return PTR_ERR(fpga_region_class);

+ fpga_region_class->dev_groups = fpga_region_groups;
fpga_region_class->dev_release = fpga_region_dev_release;

return 0;
diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
index f2e215b..e8ad979 100644
--- a/include/linux/fpga/fpga-region.h
+++ b/include/linux/fpga/fpga-region.h
@@ -12,6 +12,7 @@
* @bridge_list: list of FPGA bridges specified in region
* @mgr: FPGA manager
* @info: FPGA image info
+ * @compat_id: FPGA region id for compatibility check.
* @priv: private data
* @get_bridges: optional function to get bridges to a list
*/
@@ -21,6 +22,7 @@ struct fpga_region {
struct list_head bridge_list;
struct fpga_manager *mgr;
struct fpga_image_info *info;
+ struct fpga_compat_id *compat_id;
void *priv;
int (*get_bridges)(struct fpga_region *region);
};
--
1.8.3.1


2018-05-02 03:04:06

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 28/28] fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support

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 DFL_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:
* DFL_FPGA_PORT_DMA_MAP
Do the dma mapping per user_addr and length which provided by user.
Return iova in provided struct dfl_fpga_port_dma_map.

* DFL_FPGA_PORT_DMA_UNMAP
Unmap the dma region per iova provided by user.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
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
v4: rebase and fix SPDX license issue
v5: rebase, and add DFL_ prefix to IOCTL APIs.
---
drivers/fpga/Makefile | 2 +-
drivers/fpga/dfl-afu-dma-region.c | 463 ++++++++++++++++++++++++++++++++++++++
drivers/fpga/dfl-afu-main.c | 61 ++++-
drivers/fpga/dfl-afu.h | 31 ++-
include/uapi/linux/fpga-dfl.h | 37 +++
5 files changed, 591 insertions(+), 3 deletions(-)
create mode 100644 drivers/fpga/dfl-afu-dma-region.c

diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 041e3cd1..dd454ab 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o
obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o

dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
-dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o
+dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o dfl-afu-dma-region.o

# Drivers for FPGAs which implement DFL
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-afu-dma-region.c b/drivers/fpga/dfl-afu-dma-region.c
new file mode 100644
index 0000000..600fd0c
--- /dev/null
+++ b/drivers/fpga/dfl-afu-dma-region.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Accelerated Function Unit (AFU) DMA Region Management
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Wu Hao <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
+
+#include "dfl-afu.h"
+
+static void put_all_pages(struct page **pages, int npages)
+{
+ int i;
+
+ for (i = 0; i < npages; i++)
+ if (pages[i])
+ put_page(pages[i]);
+}
+
+void afu_dma_region_init(struct dfl_feature_platform_data *pdata)
+{
+ struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
+
+ afu->dma_regions = RB_ROOT;
+}
+
+/**
+ * afu_dma_adjust_locked_vm - adjust locked memory
+ * @dev: port device
+ * @npages: number of pages
+ * @incr: increase or decrease locked memory
+ *
+ * Increase or decrease the locked memory size with npages input.
+ *
+ * Return 0 on success.
+ * Return -ENOMEM if locked memory size is over the limit and no CAP_IPC_LOCK.
+ */
+static int afu_dma_adjust_locked_vm(struct device *dev, long npages, bool incr)
+{
+ unsigned long locked, lock_limit;
+ int ret = 0;
+
+ /* the task is exiting. */
+ if (!current->mm)
+ return 0;
+
+ down_write(&current->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(&current->mm->mmap_sem);
+
+ return ret;
+}
+
+/**
+ * afu_dma_pin_pages - pin pages of given dma memory region
+ * @pdata: feature device platform data
+ * @region: dma memory region to be pinned
+ *
+ * Pin all the pages of given dfl_afu_dma_region.
+ * Return 0 for success or negative error code.
+ */
+static int afu_dma_pin_pages(struct dfl_feature_platform_data *pdata,
+ struct dfl_afu_dma_region *region)
+{
+ int npages = region->length >> PAGE_SHIFT;
+ struct device *dev = &pdata->dev->dev;
+ int ret, pinned;
+
+ ret = afu_dma_adjust_locked_vm(dev, npages, true);
+ if (ret)
+ return ret;
+
+ region->pages = kcalloc(npages, sizeof(struct page *), GFP_KERNEL);
+ if (!region->pages) {
+ ret = -ENOMEM;
+ goto unlock_vm;
+ }
+
+ pinned = get_user_pages_fast(region->user_addr, npages, 1,
+ region->pages);
+ if (pinned < 0) {
+ ret = pinned;
+ goto put_pages;
+ } else if (pinned != npages) {
+ ret = -EFAULT;
+ goto free_pages;
+ }
+
+ dev_dbg(dev, "%d pages pinned\n", pinned);
+
+ return 0;
+
+put_pages:
+ put_all_pages(region->pages, pinned);
+free_pages:
+ kfree(region->pages);
+unlock_vm:
+ afu_dma_adjust_locked_vm(dev, npages, false);
+ return ret;
+}
+
+/**
+ * afu_dma_unpin_pages - unpin pages of given dma memory region
+ * @pdata: feature device platform data
+ * @region: dma memory region to be unpinned
+ *
+ * Unpin all the pages of given dfl_afu_dma_region.
+ * Return 0 for success or negative error code.
+ */
+static void afu_dma_unpin_pages(struct dfl_feature_platform_data *pdata,
+ struct dfl_afu_dma_region *region)
+{
+ long npages = region->length >> PAGE_SHIFT;
+ struct device *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 - check if pages are continuous
+ * @region: dma memory region
+ *
+ * Return true if pages of given dma memory region have continuous physical
+ * address, otherwise return false.
+ */
+static bool afu_dma_check_continuous_pages(struct dfl_afu_dma_region *region)
+{
+ int npages = region->length >> PAGE_SHIFT;
+ int i;
+
+ for (i = 0; i < npages - 1; i++)
+ if (page_to_pfn(region->pages[i]) + 1 !=
+ page_to_pfn(region->pages[i + 1]))
+ return false;
+
+ return true;
+}
+
+/**
+ * dma_region_check_iova - check if memory area is fully contained in the region
+ * @region: dma memory region
+ * @iova: address of the dma memory area
+ * @size: size of the dma memory area
+ *
+ * Compare the dma memory area defined by @iova and @size with given dma region.
+ * Return true if memory area is fully contained in the region, otherwise false.
+ */
+static bool dma_region_check_iova(struct dfl_afu_dma_region *region,
+ u64 iova, u64 size)
+{
+ if (!size && region->iova != iova)
+ return false;
+
+ return (region->iova <= iova) &&
+ (region->length + region->iova >= iova + size);
+}
+
+/**
+ * afu_dma_region_add - add given dma region to rbtree
+ * @pdata: feature device platform data
+ * @region: dma region to be added
+ *
+ * Return 0 for success, -EEXIST if dma region has already been added.
+ *
+ * Needs to be called with pdata->lock heold.
+ */
+static int afu_dma_region_add(struct dfl_feature_platform_data *pdata,
+ struct dfl_afu_dma_region *region)
+{
+ struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
+ struct rb_node **new, *parent = NULL;
+
+ dev_dbg(&pdata->dev->dev, "add region (iova = %llx)\n",
+ (unsigned long long)region->iova);
+
+ new = &afu->dma_regions.rb_node;
+
+ while (*new) {
+ struct dfl_afu_dma_region *this;
+
+ this = container_of(*new, struct dfl_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);
+ else if (region->iova > this->iova)
+ new = &((*new)->rb_right);
+ else
+ return -EEXIST;
+ }
+
+ rb_link_node(&region->node, parent, new);
+ rb_insert_color(&region->node, &afu->dma_regions);
+
+ return 0;
+}
+
+/**
+ * afu_dma_region_remove - remove given dma region from rbtree
+ * @pdata: feature device platform data
+ * @region: dma region to be removed
+ *
+ * Needs to be called with pdata->lock heold.
+ */
+static void afu_dma_region_remove(struct dfl_feature_platform_data *pdata,
+ struct dfl_afu_dma_region *region)
+{
+ struct dfl_afu *afu;
+
+ dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n",
+ (unsigned long long)region->iova);
+
+ afu = dfl_fpga_pdata_get_private(pdata);
+ rb_erase(&region->node, &afu->dma_regions);
+}
+
+/**
+ * afu_dma_region_destroy - destroy all regions in rbtree
+ * @pdata: feature device platform data
+ *
+ * Needs to be called with pdata->lock heold.
+ */
+void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata)
+{
+ struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
+ struct rb_node *node = rb_first(&afu->dma_regions);
+ struct dfl_afu_dma_region *region;
+
+ while (node) {
+ region = container_of(node, struct dfl_afu_dma_region, node);
+
+ dev_dbg(&pdata->dev->dev, "del region (iova = %llx)\n",
+ (unsigned long long)region->iova);
+
+ rb_erase(node, &afu->dma_regions);
+
+ if (region->iova)
+ dma_unmap_page(dfl_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 - find the dma region from rbtree based on iova and size
+ * @pdata: feature device platform data
+ * @iova: address of the dma memory area
+ * @size: size of the dma memory area
+ *
+ * It finds the dma region from the rbtree based on @iova and @size:
+ * - if @size == 0, it finds the dma region which starts from @iova
+ * - otherwise, it finds the dma region which fully contains
+ * [@iova, @iova+size)
+ * If nothing is matched returns NULL.
+ *
+ * Needs to be called with pdata->lock held.
+ */
+struct dfl_afu_dma_region *
+afu_dma_region_find(struct dfl_feature_platform_data *pdata, u64 iova, u64 size)
+{
+ struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
+ struct rb_node *node = afu->dma_regions.rb_node;
+ struct device *dev = &pdata->dev->dev;
+
+ while (node) {
+ struct dfl_afu_dma_region *region;
+
+ region = container_of(node, struct dfl_afu_dma_region, node);
+
+ if (dma_region_check_iova(region, iova, size)) {
+ dev_dbg(dev, "find region (iova = %llx)\n",
+ (unsigned long long)region->iova);
+ return region;
+ }
+
+ if (iova < region->iova)
+ node = node->rb_left;
+ else if (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",
+ (unsigned long long)iova, (unsigned long long)size);
+
+ return NULL;
+}
+
+/**
+ * afu_dma_region_find_iova - find the dma region from rbtree by iova
+ * @pdata: feature device platform data
+ * @iova: address of the dma region
+ *
+ * Needs to be called with pdata->lock held.
+ */
+static struct dfl_afu_dma_region *
+afu_dma_region_find_iova(struct dfl_feature_platform_data *pdata, u64 iova)
+{
+ return afu_dma_region_find(pdata, iova, 0);
+}
+
+/**
+ * afu_dma_map_region - map memory region for dma
+ * @pdata: feature device platform data
+ * @user_addr: address of the memory region
+ * @length: size of the memory region
+ * @iova: pointer of iova address
+ *
+ * Map memory region defined by @user_addr and @length, and return dma address
+ * of the memory region via @iova.
+ * Return 0 for success, otherwise error code.
+ */
+int afu_dma_map_region(struct dfl_feature_platform_data *pdata,
+ u64 user_addr, u64 length, u64 *iova)
+{
+ struct dfl_afu_dma_region *region;
+ int ret;
+
+ /*
+ * Check Inputs, only accept page-aligned user memory region with
+ * valid length.
+ */
+ 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 *)(unsigned long)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");
+ goto free_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;
+ goto unpin_pages;
+ }
+
+ /* As pages are continuous then start to do DMA mapping */
+ region->iova = dma_map_page(dfl_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;
+ goto unpin_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");
+ goto unmap_dma;
+ }
+
+ return 0;
+
+unmap_dma:
+ dma_unmap_page(dfl_fpga_pdata_to_parent(pdata),
+ region->iova, region->length, DMA_BIDIRECTIONAL);
+unpin_pages:
+ afu_dma_unpin_pages(pdata, region);
+free_region:
+ kfree(region);
+ return ret;
+}
+
+/**
+ * afu_dma_unmap_region - unmap dma memory region
+ * @pdata: feature device platform data
+ * @iova: dma address of the region
+ *
+ * Unmap dma memory region based on @iova.
+ * Return 0 for success, otherwise error code.
+ */
+int afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova)
+{
+ struct dfl_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(dfl_fpga_pdata_to_parent(pdata),
+ region->iova, region->length, DMA_BIDIRECTIONAL);
+ afu_dma_unpin_pages(pdata, region);
+ kfree(region);
+
+ return 0;
+}
diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
index e7024dd..56241b8 100644
--- a/drivers/fpga/dfl-afu-main.c
+++ b/drivers/fpga/dfl-afu-main.c
@@ -293,7 +293,11 @@ static int afu_release(struct inode *inode, struct file *filp)

pdata = dev_get_platdata(&pdev->dev);

- port_reset(pdev);
+ mutex_lock(&pdata->lock);
+ __port_reset(pdev);
+ afu_dma_region_destroy(pdata);
+ mutex_unlock(&pdata->lock);
+
dfl_feature_dev_use_end(pdata);

return 0;
@@ -364,6 +368,55 @@ static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
return 0;
}

+static long
+afu_ioctl_dma_map(struct dfl_feature_platform_data *pdata, void __user *arg)
+{
+ struct dfl_fpga_port_dma_map map;
+ unsigned long minsz;
+ long ret;
+
+ minsz = offsetofend(struct dfl_fpga_port_dma_map, iova);
+
+ if (copy_from_user(&map, arg, minsz))
+ return -EFAULT;
+
+ if (map.argsz < minsz || map.flags)
+ return -EINVAL;
+
+ ret = afu_dma_map_region(pdata, map.user_addr, map.length, &map.iova);
+ if (ret)
+ return ret;
+
+ if (copy_to_user(arg, &map, sizeof(map))) {
+ afu_dma_unmap_region(pdata, map.iova);
+ return -EFAULT;
+ }
+
+ dev_dbg(&pdata->dev->dev, "dma map: ua=%llx, len=%llx, iova=%llx\n",
+ (unsigned long long)map.user_addr,
+ (unsigned long long)map.length,
+ (unsigned long long)map.iova);
+
+ return 0;
+}
+
+static long
+afu_ioctl_dma_unmap(struct dfl_feature_platform_data *pdata, void __user *arg)
+{
+ struct dfl_fpga_port_dma_unmap unmap;
+ unsigned long minsz;
+
+ minsz = offsetofend(struct dfl_fpga_port_dma_unmap, iova);
+
+ if (copy_from_user(&unmap, arg, minsz))
+ return -EFAULT;
+
+ if (unmap.argsz < minsz || unmap.flags)
+ return -EINVAL;
+
+ return afu_dma_unmap_region(pdata, unmap.iova);
+}
+
static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct platform_device *pdev = filp->private_data;
@@ -384,6 +437,10 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return afu_ioctl_get_info(pdata, (void __user *)arg);
case DFL_FPGA_PORT_GET_REGION_INFO:
return afu_ioctl_get_region_info(pdata, (void __user *)arg);
+ case DFL_FPGA_PORT_DMA_MAP:
+ return afu_ioctl_dma_map(pdata, (void __user *)arg);
+ case DFL_FPGA_PORT_DMA_UNMAP:
+ return afu_ioctl_dma_unmap(pdata, (void __user *)arg);
default:
/*
* Let sub-feature's ioctl function to handle the cmd
@@ -460,6 +517,7 @@ static int afu_dev_init(struct platform_device *pdev)
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, afu);
afu_mmio_region_init(pdata);
+ afu_dma_region_init(pdata);
mutex_unlock(&pdata->lock);

return 0;
@@ -473,6 +531,7 @@ static int afu_dev_destroy(struct platform_device *pdev)
mutex_lock(&pdata->lock);
afu = dfl_fpga_pdata_get_private(pdata);
afu_mmio_region_destroy(pdata);
+ afu_dma_region_destroy(pdata);
dfl_fpga_pdata_set_private(pdata, NULL);
mutex_unlock(&pdata->lock);

diff --git a/drivers/fpga/dfl-afu.h b/drivers/fpga/dfl-afu.h
index 7376af9..42d26b9 100644
--- a/drivers/fpga/dfl-afu.h
+++ b/drivers/fpga/dfl-afu.h
@@ -41,11 +41,31 @@ struct dfl_afu_mmio_region {
};

/**
+ * struct fpga_afu_dma_region - afu DMA region data structure
+ *
+ * @user_addr: region userspace virtual address.
+ * @length: region length.
+ * @iova: region IO virtual address.
+ * @pages: ptr to pages of this region.
+ * @node: rb tree node.
+ * @in_use: flag to indicate if this region is in_use.
+ */
+struct dfl_afu_dma_region {
+ u64 user_addr;
+ u64 length;
+ u64 iova;
+ struct page **pages;
+ struct rb_node node;
+ bool in_use;
+};
+
+/**
* struct dfl_afu - afu device data structure
*
* @region_cur_offset: current region offset from start to the device fd.
* @num_regions: num of mmio regions.
* @regions: the mmio region link list of this afu feature device.
+ * @dma_regions: root of dma regions rb tree.
* @num_umsgs: num of umsgs.
* @pdata: afu platform device's pdata.
*/
@@ -54,6 +74,7 @@ struct dfl_afu {
int num_regions;
u8 num_umsgs;
struct list_head regions;
+ struct rb_root dma_regions;

struct dfl_feature_platform_data *pdata;
};
@@ -68,4 +89,12 @@ int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
u64 offset, u64 size,
struct dfl_afu_mmio_region *pregion);
-#endif
+void afu_dma_region_init(struct dfl_feature_platform_data *pdata);
+void afu_dma_region_destroy(struct dfl_feature_platform_data *pdata);
+int afu_dma_map_region(struct dfl_feature_platform_data *pdata,
+ u64 user_addr, u64 length, u64 *iova);
+int afu_dma_unmap_region(struct dfl_feature_platform_data *pdata, u64 iova);
+struct dfl_afu_dma_region *
+afu_dma_region_find(struct dfl_feature_platform_data *pdata,
+ u64 iova, u64 size);
+#endif /* __DFL_AFU_H */
diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h
index 8a7dd5e..77af6c1 100644
--- a/include/uapi/linux/fpga-dfl.h
+++ b/include/uapi/linux/fpga-dfl.h
@@ -113,6 +113,43 @@ struct dfl_fpga_port_region_info {

#define DFL_FPGA_PORT_GET_REGION_INFO _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 2)

+/**
+ * DFL_FPGA_PORT_DMA_MAP - _IOWR(DFL_FPGA_MAGIC, DFL_PORT_BASE + 3,
+ * struct dfl_fpga_port_dma_map)
+ *
+ * Map the dma memory per user_addr and length which are provided by caller.
+ * Driver fills the iova in provided struct afu_port_dma_map.
+ * This interface only accepts page-size aligned user memory for dma mapping.
+ * Return: 0 on success, -errno on failure.
+ */
+struct dfl_fpga_port_dma_map {
+ /* Input */
+ __u32 argsz; /* Structure length */
+ __u32 flags; /* Zero for now */
+ __u64 user_addr; /* Process virtual address */
+ __u64 length; /* Length of mapping (bytes)*/
+ /* Output */
+ __u64 iova; /* IO virtual address */
+};
+
+#define DFL_FPGA_PORT_DMA_MAP _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 3)
+
+/**
+ * DFL_FPGA_PORT_DMA_UNMAP - _IOW(FPGA_MAGIC, PORT_BASE + 4,
+ * struct dfl_fpga_port_dma_unmap)
+ *
+ * Unmap the dma memory per iova provided by caller.
+ * Return: 0 on success, -errno on failure.
+ */
+struct dfl_fpga_port_dma_unmap {
+ /* Input */
+ __u32 argsz; /* Structure length */
+ __u32 flags; /* Zero for now */
+ __u64 iova; /* IO virtual address */
+};
+
+#define DFL_FPGA_PORT_DMA_UNMAP _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 4)
+
/* IOCTLs for FME file descriptor */

/**
--
1.8.3.1


2018-05-02 03:04:13

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 27/28] fpga: dfl: afu: add afu sub feature support

From: Xiao Guangrong <[email protected]>

User Accelerated Function Unit sub feature exposes the MMIO region of
the AFU. After valid PR bitstream is programmed and the 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>/<dfl-port.x>/afu_id
Read-only. Indicate which PR bitstream is programmed to this AFU.

Ioctl interfaces:
* DFL_FPGA_PORT_GET_INFO
Provide info to userspace on the number of supported region.
Only UAFU region is supported now.

* DFL_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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
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
v4: rebase and fix SPDX license issue
v5: rebase, due to DFL framework naming changes.
add DFL_ prefix to IOCTL API.
remove "user" afu, and replace "green bitstream" terminology.
add "mmio" to afu region functions and data structures.
---
Documentation/ABI/testing/sysfs-platform-dfl-port | 9 +
drivers/fpga/Makefile | 2 +-
drivers/fpga/dfl-afu-main.c | 219 +++++++++++++++++++++-
drivers/fpga/dfl-afu-region.c | 166 ++++++++++++++++
drivers/fpga/dfl-afu.h | 71 +++++++
include/uapi/linux/fpga-dfl.h | 48 +++++
6 files changed, 508 insertions(+), 7 deletions(-)
create mode 100644 drivers/fpga/dfl-afu-region.c
create mode 100644 drivers/fpga/dfl-afu.h

diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-port b/Documentation/ABI/testing/sysfs-platform-dfl-port
index eb1f02d..739a3fb 100644
--- a/Documentation/ABI/testing/sysfs-platform-dfl-port
+++ b/Documentation/ABI/testing/sysfs-platform-dfl-port
@@ -5,3 +5,12 @@ Contact: Wu Hao <[email protected]>
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/dfl-port.0/afu_id
+Date: May 2018
+KernelVersion: 4.17
+Contact: Wu Hao <[email protected]>
+Description: Read-only. User can program different PR bitstreams to FPGA
+ Accelerator Function Unit (AFU) for different functions. It
+ returns uuid which could be used to identify which PR bitstream
+ is programmed in this AFU.
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 5c9607b..041e3cd1 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -37,7 +37,7 @@ obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o
obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o

dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
-dfl-afu-objs := dfl-afu-main.o
+dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o

# Drivers for FPGAs which implement DFL
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
index 57bb726..e7024dd 100644
--- a/drivers/fpga/dfl-afu-main.c
+++ b/drivers/fpga/dfl-afu-main.c
@@ -16,18 +16,18 @@

#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/uaccess.h>
#include <linux/fpga-dfl.h>

-#include "dfl.h"
+#include "dfl-afu.h"

/**
* port_enable - enable a port
* @pdev: port platform device.
*
* 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.
- * port_enable function should only be used after port_disable
- * function.
+ * The AFU is unable to respond to any MMIO access while in reset.
+ * port_enable function should only be used after port_disable function.
*/
static void port_enable(struct platform_device *pdev)
{
@@ -191,12 +191,75 @@ static void port_hdr_uinit(struct platform_device *pdev,
.ioctl = port_hdr_ioctl,
};

+static ssize_t
+afu_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+ void __iomem *base;
+ u64 guidl, guidh;
+
+ base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU);
+
+ mutex_lock(&pdata->lock);
+ if (pdata->disable_count) {
+ mutex_unlock(&pdata->lock);
+ return -EBUSY;
+ }
+
+ guidl = readq(base + GUID_L);
+ guidh = readq(base + GUID_H);
+ mutex_unlock(&pdata->lock);
+
+ return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl);
+}
+static DEVICE_ATTR_RO(afu_id);
+
+static const struct attribute *port_afu_attrs[] = {
+ &dev_attr_afu_id.attr,
+ NULL
+};
+
+static int port_afu_init(struct platform_device *pdev,
+ struct dfl_feature *feature)
+{
+ struct resource *res = &pdev->resource[feature->resource_index];
+ int ret;
+
+ dev_dbg(&pdev->dev, "PORT AFU Init.\n");
+
+ ret = afu_mmio_region_add(dev_get_platdata(&pdev->dev),
+ DFL_PORT_REGION_INDEX_AFU, resource_size(res),
+ res->start, DFL_PORT_REGION_READ |
+ DFL_PORT_REGION_WRITE | DFL_PORT_REGION_MMAP);
+ if (ret)
+ return ret;
+
+ return sysfs_create_files(&pdev->dev.kobj, port_afu_attrs);
+}
+
+static void port_afu_uinit(struct platform_device *pdev,
+ struct dfl_feature *feature)
+{
+ dev_dbg(&pdev->dev, "PORT AFU UInit.\n");
+
+ sysfs_remove_files(&pdev->dev.kobj, port_afu_attrs);
+}
+
+static const struct dfl_feature_ops port_afu_ops = {
+ .init = port_afu_init,
+ .uinit = port_afu_uinit,
+};
+
static struct dfl_feature_driver port_feature_drvs[] = {
{
.id = PORT_FEATURE_ID_HEADER,
.ops = &port_hdr_ops,
},
{
+ .id = PORT_FEATURE_ID_AFU,
+ .ops = &port_afu_ops,
+ },
+ {
.ops = NULL,
}
};
@@ -243,6 +306,64 @@ static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
return 0;
}

+static long
+afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
+{
+ struct dfl_fpga_port_info info;
+ struct dfl_afu *afu;
+ unsigned long minsz;
+
+ minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs);
+
+ if (copy_from_user(&info, arg, minsz))
+ return -EFAULT;
+
+ if (info.argsz < minsz)
+ return -EINVAL;
+
+ mutex_lock(&pdata->lock);
+ afu = dfl_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;
+
+ return 0;
+}
+
+static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
+ void __user *arg)
+{
+ struct dfl_fpga_port_region_info rinfo;
+ struct dfl_afu_mmio_region region;
+ unsigned long minsz;
+ long ret;
+
+ minsz = offsetofend(struct dfl_fpga_port_region_info, offset);
+
+ if (copy_from_user(&rinfo, arg, minsz))
+ return -EFAULT;
+
+ if (rinfo.argsz < minsz || rinfo.padding)
+ return -EINVAL;
+
+ ret = afu_mmio_region_get_by_index(pdata, rinfo.index, &region);
+ if (ret)
+ return ret;
+
+ rinfo.flags = region.flags;
+ rinfo.size = region.size;
+ rinfo.offset = region.offset;
+
+ if (copy_to_user(arg, &rinfo, sizeof(rinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct platform_device *pdev = filp->private_data;
@@ -259,6 +380,10 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return DFL_FPGA_API_VERSION;
case DFL_FPGA_CHECK_EXTENSION:
return afu_ioctl_check_extension(pdata, arg);
+ case DFL_FPGA_PORT_GET_INFO:
+ return afu_ioctl_get_info(pdata, (void __user *)arg);
+ case DFL_FPGA_PORT_GET_REGION_INFO:
+ return afu_ioctl_get_region_info(pdata, (void __user *)arg);
default:
/*
* Let sub-feature's ioctl function to handle the cmd
@@ -277,13 +402,83 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return -EINVAL;
}

+static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct platform_device *pdev = filp->private_data;
+ struct dfl_feature_platform_data *pdata;
+ u64 size = vma->vm_end - vma->vm_start;
+ struct dfl_afu_mmio_region region;
+ u64 offset;
+ int ret;
+
+ if (!(vma->vm_flags & VM_SHARED))
+ return -EINVAL;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ offset = vma->vm_pgoff << PAGE_SHIFT;
+ ret = afu_mmio_region_get_by_offset(pdata, offset, size, &region);
+ if (ret)
+ return ret;
+
+ if (!(region.flags & DFL_PORT_REGION_MMAP))
+ return -EINVAL;
+
+ if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ))
+ return -EPERM;
+
+ if ((vma->vm_flags & VM_WRITE) &&
+ !(region.flags & DFL_PORT_REGION_WRITE))
+ return -EPERM;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ return remap_pfn_range(vma, vma->vm_start,
+ (region.phys + (offset - region.offset)) >> PAGE_SHIFT,
+ size, vma->vm_page_prot);
+}
+
static const struct file_operations afu_fops = {
.owner = THIS_MODULE,
.open = afu_open,
.release = afu_release,
.unlocked_ioctl = afu_ioctl,
+ .mmap = afu_mmap,
};

+static int afu_dev_init(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_afu *afu;
+
+ afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL);
+ if (!afu)
+ return -ENOMEM;
+
+ afu->pdata = pdata;
+
+ mutex_lock(&pdata->lock);
+ dfl_fpga_pdata_set_private(pdata, afu);
+ afu_mmio_region_init(pdata);
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
+static int afu_dev_destroy(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_afu *afu;
+
+ mutex_lock(&pdata->lock);
+ afu = dfl_fpga_pdata_get_private(pdata);
+ afu_mmio_region_destroy(pdata);
+ dfl_fpga_pdata_set_private(pdata, NULL);
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
static int port_enable_set(struct platform_device *pdev, bool enable)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -312,14 +507,25 @@ static int afu_probe(struct platform_device *pdev)

dev_dbg(&pdev->dev, "%s\n", __func__);

+ ret = afu_dev_init(pdev);
+ if (ret)
+ goto exit;
+
ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
if (ret)
- return ret;
+ goto dev_destroy;

ret = dfl_fpga_register_dev_ops(pdev, &afu_fops, THIS_MODULE);
- if (ret)
+ if (ret) {
dfl_fpga_dev_feature_uinit(pdev);
+ goto dev_destroy;
+ }
+
+ return 0;

+dev_destroy:
+ afu_dev_destroy(pdev);
+exit:
return ret;
}

@@ -329,6 +535,7 @@ static int afu_remove(struct platform_device *pdev)

dfl_fpga_unregister_dev_ops(pdev);
dfl_fpga_dev_feature_uinit(pdev);
+ afu_dev_destroy(pdev);

return 0;
}
diff --git a/drivers/fpga/dfl-afu-region.c b/drivers/fpga/dfl-afu-region.c
new file mode 100644
index 0000000..2f34f92
--- /dev/null
+++ b/drivers/fpga/dfl-afu-region.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Accelerated Function Unit (AFU) MMIO Region Management
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Wu Hao <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ */
+#include "dfl-afu.h"
+
+/**
+ * afu_mmio_region_init - init function for afu mmio region support
+ * @pdata: afu platform device's pdata.
+ */
+void afu_mmio_region_init(struct dfl_feature_platform_data *pdata)
+{
+ struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
+
+ INIT_LIST_HEAD(&afu->regions);
+}
+
+#define for_each_region(region, afu) \
+ list_for_each_entry((region), &(afu)->regions, node)
+
+static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu,
+ u32 region_index)
+{
+ struct dfl_afu_mmio_region *region;
+
+ for_each_region(region, afu)
+ if (region->index == region_index)
+ return region;
+
+ return NULL;
+}
+
+/**
+ * afu_mmio_region_add - add a mmio region to given feature dev.
+ *
+ * @region_index: region index.
+ * @region_size: region size.
+ * @phys: region's physical address of this region.
+ * @flags: region flags (access permission).
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
+ u32 region_index, u64 region_size, u64 phys, u32 flags)
+{
+ struct dfl_afu_mmio_region *region;
+ struct dfl_afu *afu;
+ int ret = 0;
+
+ region = devm_kzalloc(&pdata->dev->dev, sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ region->index = region_index;
+ region->size = region_size;
+ region->phys = phys;
+ region->flags = flags;
+
+ mutex_lock(&pdata->lock);
+
+ afu = dfl_fpga_pdata_get_private(pdata);
+
+ /* check if @index already exists */
+ if (get_region_by_index(afu, region_index)) {
+ mutex_unlock(&pdata->lock);
+ ret = -EEXIST;
+ goto exit;
+ }
+
+ region_size = PAGE_ALIGN(region_size);
+ region->offset = afu->region_cur_offset;
+ list_add(&region->node, &afu->regions);
+
+ afu->region_cur_offset += region_size;
+ afu->num_regions++;
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+
+exit:
+ devm_kfree(&pdata->dev->dev, region);
+ return ret;
+}
+
+/**
+ * afu_mmio_region_destroy - destroy all mmio regions under given feature dev.
+ * @pdata: afu platform device's pdata.
+ */
+void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata)
+{
+ struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
+ struct dfl_afu_mmio_region *tmp, *region;
+
+ list_for_each_entry_safe(region, tmp, &afu->regions, node)
+ devm_kfree(&pdata->dev->dev, region);
+}
+
+/**
+ * afu_mmio_region_get_by_index - find an afu region by index.
+ * @pdata: afu platform device's pdata.
+ * @region_index: region index.
+ * @pregion: ptr to region for result.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
+ u32 region_index,
+ struct dfl_afu_mmio_region *pregion)
+{
+ struct dfl_afu_mmio_region *region;
+ struct dfl_afu *afu;
+ int ret = 0;
+
+ mutex_lock(&pdata->lock);
+ afu = dfl_fpga_pdata_get_private(pdata);
+ region = get_region_by_index(afu, region_index);
+ if (!region) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ *pregion = *region;
+exit:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+/**
+ * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size
+ *
+ * @pdata: afu platform device's pdata.
+ * @offset: region offset from start of the device fd.
+ * @size: region size.
+ * @pregion: ptr to region for result.
+ *
+ * Find the region which fully contains the region described by input
+ * parameters (offset and size) from the feature dev's region link list.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
+ u64 offset, u64 size,
+ struct dfl_afu_mmio_region *pregion)
+{
+ struct dfl_afu_mmio_region *region;
+ struct dfl_afu *afu;
+ int ret = 0;
+
+ mutex_lock(&pdata->lock);
+ afu = dfl_fpga_pdata_get_private(pdata);
+ for_each_region(region, afu)
+ if (region->offset <= offset &&
+ region->offset + region->size >= offset + size) {
+ *pregion = *region;
+ goto exit;
+ }
+ ret = -EINVAL;
+exit:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
diff --git a/drivers/fpga/dfl-afu.h b/drivers/fpga/dfl-afu.h
new file mode 100644
index 0000000..7376af9
--- /dev/null
+++ b/drivers/fpga/dfl-afu.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for FPGA Accelerated Function Unit (AFU) Driver
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Wu Hao <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#ifndef __DFL_AFU_H
+#define __DFL_AFU_H
+
+#include <linux/mm.h>
+
+#include "dfl.h"
+
+/**
+ * struct dfl_afu_mmio_region - afu mmio region data structure
+ *
+ * @index: region index.
+ * @flags: region flags (access permission).
+ * @size: region size.
+ * @offset: region offset from start of the device fd.
+ * @phys: region's physical address.
+ * @node: node to add to afu feature dev's region list.
+ */
+struct dfl_afu_mmio_region {
+ u32 index;
+ u32 flags;
+ u64 size;
+ u64 offset;
+ u64 phys;
+ struct list_head node;
+};
+
+/**
+ * struct dfl_afu - afu device data structure
+ *
+ * @region_cur_offset: current region offset from start to the device fd.
+ * @num_regions: num of mmio regions.
+ * @regions: the mmio region link list of this afu feature device.
+ * @num_umsgs: num of umsgs.
+ * @pdata: afu platform device's pdata.
+ */
+struct dfl_afu {
+ u64 region_cur_offset;
+ int num_regions;
+ u8 num_umsgs;
+ struct list_head regions;
+
+ struct dfl_feature_platform_data *pdata;
+};
+
+void afu_mmio_region_init(struct dfl_feature_platform_data *pdata);
+int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
+ u32 region_index, u64 region_size, u64 phys, u32 flags);
+void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata);
+int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
+ u32 region_index,
+ struct dfl_afu_mmio_region *pregion);
+int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
+ u64 offset, u64 size,
+ struct dfl_afu_mmio_region *pregion);
+#endif
diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h
index 777d6ac..8a7dd5e 100644
--- a/include/uapi/linux/fpga-dfl.h
+++ b/include/uapi/linux/fpga-dfl.h
@@ -65,6 +65,54 @@

#define DFL_FPGA_PORT_RESET _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 0)

+/**
+ * DFL_FPGA_PORT_GET_INFO - _IOR(DFL_FPGA_MAGIC, DFL_PORT_BASE + 1,
+ * struct dfl_fpga_port_info)
+ *
+ * Retrieve information about the fpga port.
+ * Driver fills the info in provided struct dfl_fpga_port_info.
+ * Return: 0 on success, -errno on failure.
+ */
+struct dfl_fpga_port_info {
+ /* Input */
+ __u32 argsz; /* Structure length */
+ /* Output */
+ __u32 flags; /* Zero for now */
+ __u32 num_regions; /* The number of supported regions */
+ __u32 num_umsgs; /* The number of allocated umsgs */
+};
+
+#define DFL_FPGA_PORT_GET_INFO _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 1)
+
+/**
+ * FPGA_PORT_GET_REGION_INFO - _IOWR(FPGA_MAGIC, PORT_BASE + 2,
+ * struct dfl_fpga_port_region_info)
+ *
+ * Retrieve information about a device memory region.
+ * Caller provides struct dfl_fpga_port_region_info with index value set.
+ * Driver returns the region info in other fields.
+ * Return: 0 on success, -errno on failure.
+ */
+struct dfl_fpga_port_region_info {
+ /* input */
+ __u32 argsz; /* Structure length */
+ /* Output */
+ __u32 flags; /* Access permission */
+#define DFL_PORT_REGION_READ (1 << 0) /* Region is readable */
+#define DFL_PORT_REGION_WRITE (1 << 1) /* Region is writable */
+#define DFL_PORT_REGION_MMAP (1 << 2) /* Can be mmaped to userspace */
+ /* Input */
+ __u32 index; /* Region index */
+#define DFL_PORT_REGION_INDEX_AFU 0 /* AFU */
+#define DFL_PORT_REGION_INDEX_STP 1 /* Signal Tap */
+ __u32 padding;
+ /* Output */
+ __u64 size; /* Region size (bytes) */
+ __u64 offset; /* Region offset from start of device fd */
+};
+
+#define DFL_FPGA_PORT_GET_REGION_INFO _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 2)
+
/* IOCTLs for FME file descriptor */

/**
--
1.8.3.1


2018-05-02 03:04:33

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 23/28] fpga: dfl: add FPGA Accelerated Function Unit driver basic framework

On DFL FPGA devices, the Accelerated Function Unit (AFU), can be
reprogrammed for different functions. It connects to the FPGA
infrastructure (static FPGA region) via a Port. Port CSRs are
implemented separately from the AFU CSRs to provide control and
status of the Port. Once valid PR 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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
---
v3: rename driver to dfl-afu-main.c
v4: rename to dfl-port and fix SPDX license issue.
v5: rebase, and add Acked-by from Alan.
fix uinit order in remove function.
---
drivers/fpga/Kconfig | 9 +++
drivers/fpga/Makefile | 2 +
drivers/fpga/dfl-afu-main.c | 162 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 173 insertions(+)
create mode 100644 drivers/fpga/dfl-afu-main.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 65d54a4..4c6b45f 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -168,6 +168,15 @@ config FPGA_DFL_FME_REGION
help
Say Y to enable FPGA Region driver for FPGA Management Engine.

+config FPGA_DFL_AFU
+ tristate "FPGA DFL AFU Driver"
+ depends on FPGA_DFL
+ help
+ This is the driver for FPGA Accelerated Function Unit (AFU) which
+ implements AFU and Port management features. A User AFU connects
+ to the FPGA infrastructure via a Port. There may be more than 1
+ Port/AFU per DFL based FPGA device.
+
config FPGA_DFL_PCI
tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
depends on PCI && FPGA_DFL
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 163894e..5c9607b 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -34,8 +34,10 @@ obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o
+obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o

dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
+dfl-afu-objs := dfl-afu-main.o

# Drivers for FPGAs which implement DFL
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
new file mode 100644
index 0000000..35bec47
--- /dev/null
+++ b/drivers/fpga/dfl-afu-main.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Accelerated Function Unit (AFU)
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Wu Hao <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "dfl.h"
+
+static int port_hdr_init(struct platform_device *pdev,
+ struct dfl_feature *feature)
+{
+ dev_dbg(&pdev->dev, "PORT HDR Init.\n");
+
+ return 0;
+}
+
+static void port_hdr_uinit(struct platform_device *pdev,
+ struct dfl_feature *feature)
+{
+ dev_dbg(&pdev->dev, "PORT HDR UInit.\n");
+}
+
+static const struct dfl_feature_ops port_hdr_ops = {
+ .init = port_hdr_init,
+ .uinit = port_hdr_uinit,
+};
+
+static struct dfl_feature_driver port_feature_drvs[] = {
+ {
+ .id = PORT_FEATURE_ID_HEADER,
+ .ops = &port_hdr_ops,
+ },
+ {
+ .ops = NULL,
+ }
+};
+
+static int afu_open(struct inode *inode, struct file *filp)
+{
+ struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
+ struct dfl_feature_platform_data *pdata;
+ int ret;
+
+ pdata = dev_get_platdata(&fdev->dev);
+ if (WARN_ON(!pdata))
+ return -ENODEV;
+
+ ret = dfl_feature_dev_use_begin(pdata);
+ if (ret)
+ return ret;
+
+ dev_dbg(&fdev->dev, "Device File Open\n");
+ filp->private_data = fdev;
+
+ return 0;
+}
+
+static int afu_release(struct inode *inode, struct file *filp)
+{
+ struct platform_device *pdev = filp->private_data;
+ struct dfl_feature_platform_data *pdata;
+
+ dev_dbg(&pdev->dev, "Device File Release\n");
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ dfl_feature_dev_use_end(pdata);
+
+ return 0;
+}
+
+static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct platform_device *pdev = filp->private_data;
+ struct dfl_feature_platform_data *pdata;
+ struct dfl_feature *f;
+ long ret;
+
+ dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ switch (cmd) {
+ default:
+ /*
+ * Let sub-feature's ioctl function to handle the cmd
+ * Sub-feature's ioctl returns -ENODEV when cmd is not
+ * handled in this sub feature, and returns 0 and other
+ * error code if cmd is handled.
+ */
+ dfl_fpga_dev_for_each_feature(pdata, f)
+ if (f->ops && f->ops->ioctl) {
+ ret = f->ops->ioctl(pdev, f, cmd, arg);
+ if (ret != -ENODEV)
+ return ret;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct file_operations afu_fops = {
+ .owner = THIS_MODULE,
+ .open = afu_open,
+ .release = afu_release,
+ .unlocked_ioctl = afu_ioctl,
+};
+
+static int afu_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
+ if (ret)
+ return ret;
+
+ ret = dfl_fpga_register_dev_ops(pdev, &afu_fops, THIS_MODULE);
+ if (ret)
+ dfl_fpga_dev_feature_uinit(pdev);
+
+ return ret;
+}
+
+static int afu_remove(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ dfl_fpga_unregister_dev_ops(pdev);
+ dfl_fpga_dev_feature_uinit(pdev);
+
+ return 0;
+}
+
+static struct platform_driver afu_driver = {
+ .driver = {
+ .name = DFL_FPGA_FEATURE_DEV_PORT,
+ },
+ .probe = afu_probe,
+ .remove = afu_remove,
+};
+
+module_platform_driver(afu_driver);
+
+MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dfl-port");
--
1.8.3.1


2018-05-02 03:04:51

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 26/28] fpga: dfl: afu: add DFL_FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls support

DFL_FPGA_GET_API_VERSION and DFL_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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
---
v2: rebased
v3: rebased as driver renamed to fpga-dfl-afu
fix one checkpatch issue
v4: add Acked-by from Alan and Moritz.
v5: rebase, and add DFL_ prefix to APIs.
---
drivers/fpga/dfl-afu-main.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
index 6f846b7..57bb726 100644
--- a/drivers/fpga/dfl-afu-main.c
+++ b/drivers/fpga/dfl-afu-main.c
@@ -236,6 +236,13 @@ static int afu_release(struct inode *inode, struct file *filp)
return 0;
}

+static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
+ unsigned long arg)
+{
+ /* No extension support for now */
+ return 0;
+}
+
static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct platform_device *pdev = filp->private_data;
@@ -248,6 +255,10 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
pdata = dev_get_platdata(&pdev->dev);

switch (cmd) {
+ case DFL_FPGA_GET_API_VERSION:
+ return DFL_FPGA_API_VERSION;
+ case DFL_FPGA_CHECK_EXTENSION:
+ return afu_ioctl_check_extension(pdata, arg);
default:
/*
* Let sub-feature's ioctl function to handle the cmd
--
1.8.3.1


2018-05-02 03:05:20

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 21/28] fpga: dfl: add fpga region platform driver for FME

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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
---
v3: rename driver to fpga-dfl-fme-region
fix fpga_mgr_put order problem in remove function.
rebased due to fpga api changes.
v4: rename to dfl-fme-region, fix SPDX license issue
include dfl-fme-pr.h instead of dfl-fme.h and dfl.h
add Acked-by from Alan and Moritz
v5: rebase, due to fpga region api changes.
---
drivers/fpga/Kconfig | 6 +++
drivers/fpga/Makefile | 1 +
drivers/fpga/dfl-fme-region.c | 88 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 95 insertions(+)
create mode 100644 drivers/fpga/dfl-fme-region.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index a8f939a..65d54a4 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -162,6 +162,12 @@ config FPGA_DFL_FME_BRIDGE
help
Say Y to enable FPGA Bridge driver for FPGA Management Engine.

+config FPGA_DFL_FME_REGION
+ tristate "FPGA DFL FME Region Driver"
+ depends on FPGA_DFL_FME
+ help
+ Say Y to enable FPGA Region driver for FPGA Management Engine.
+
config FPGA_DFL_PCI
tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
depends on PCI && FPGA_DFL
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 75096e9..163894e 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_FPGA_DFL) += dfl.o
obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
+obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o

dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o

diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
new file mode 100644
index 0000000..696b313
--- /dev/null
+++ b/drivers/fpga/dfl-fme-region.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * FPGA Region Driver for FPGA Management Engine (FME)
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Wu Hao <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#include <linux/module.h>
+#include <linux/fpga/fpga-region.h>
+
+#include "dfl-fme-pr.h"
+
+static int fme_region_get_bridges(struct fpga_region *region)
+{
+ struct dfl_fme_region_pdata *pdata = region->priv;
+ struct device *dev = &pdata->br->dev;
+
+ return fpga_bridge_get_to_list(dev, region->info, &region->bridge_list);
+}
+
+static int fme_region_probe(struct platform_device *pdev)
+{
+ struct dfl_fme_region_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ struct fpga_region *region;
+ struct fpga_manager *mgr;
+ int ret;
+
+ mgr = fpga_mgr_get(&pdata->mgr->dev);
+ if (IS_ERR(mgr))
+ return -EPROBE_DEFER;
+
+ region = fpga_region_create(dev, mgr, fme_region_get_bridges);
+ if (!region) {
+ ret = -ENOMEM;
+ goto eprobe_mgr_put;
+ }
+
+ region->priv = pdata;
+ platform_set_drvdata(pdev, region);
+
+ ret = fpga_region_register(region);
+ if (ret)
+ goto region_free;
+
+ dev_dbg(dev, "DFL FME FPGA Region probed\n");
+
+ return 0;
+
+region_free:
+ fpga_region_free(region);
+eprobe_mgr_put:
+ fpga_mgr_put(mgr);
+ return ret;
+}
+
+static int fme_region_remove(struct platform_device *pdev)
+{
+ struct fpga_region *region = dev_get_drvdata(&pdev->dev);
+
+ fpga_region_unregister(region);
+ fpga_mgr_put(region->mgr);
+
+ return 0;
+}
+
+static struct platform_driver fme_region_driver = {
+ .driver = {
+ .name = DFL_FPGA_FME_REGION,
+ },
+ .probe = fme_region_probe,
+ .remove = fme_region_remove,
+};
+
+module_platform_driver(fme_region_driver);
+
+MODULE_DESCRIPTION("FPGA Region for DFL FPGA Management Engine");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dfl-fme-region");
--
1.8.3.1


2018-05-02 03:05:41

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 24/28] fpga: dfl: afu: add port ops support

This patch registers the port ops into the global list in the DFL
framework, and it allows other modules to use the port ops. And
This patch includes the implementation of the get_id and enable_set
ops too.

Signed-off-by: Wu Hao <[email protected]>
---
drivers/fpga/dfl-afu-main.c | 122 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 121 insertions(+), 1 deletion(-)

diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
index 35bec47..4bdbe3d 100644
--- a/drivers/fpga/dfl-afu-main.c
+++ b/drivers/fpga/dfl-afu-main.c
@@ -19,6 +19,83 @@

#include "dfl.h"

+/**
+ * port_enable - enable a port
+ * @pdev: port platform device.
+ *
+ * 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.
+ * port_enable function should only be used after port_disable
+ * function.
+ */
+static void port_enable(struct platform_device *pdev)
+{
+ struct dfl_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 = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
+
+ /* Clear port soft reset */
+ v = readq(base + PORT_HDR_CTRL);
+ v &= ~PORT_CTRL_SFTRST;
+ writeq(v, base + PORT_HDR_CTRL);
+}
+
+#define RST_POLL_INVL 10 /* us */
+#define RST_POLL_TIMEOUT 1000 /* us */
+
+/**
+ * port_disable - disable a port
+ * @pdev: port platform device.
+ *
+ * Disable Port by setting the port soft reset bit, it puts the port into
+ * reset.
+ */
+static int port_disable(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ void __iomem *base;
+ u64 v;
+
+ if (pdata->disable_count++ != 0)
+ return 0;
+
+ base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
+
+ /* 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;
+}
+
+static int port_get_id(struct platform_device *pdev)
+{
+ void __iomem *base;
+
+ base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
+
+ return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP));
+}
+
static int port_hdr_init(struct platform_device *pdev,
struct dfl_feature *feature)
{
@@ -119,6 +196,28 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
.unlocked_ioctl = afu_ioctl,
};

+static int port_enable_set(struct platform_device *pdev, bool enable)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ int ret = 0;
+
+ mutex_lock(&pdata->lock);
+ if (enable)
+ port_enable(pdev);
+ else
+ ret = port_disable(pdev);
+ mutex_unlock(&pdata->lock);
+
+ return ret;
+}
+
+static struct dfl_fpga_port_ops afu_port_ops = {
+ .name = DFL_FPGA_FEATURE_DEV_PORT,
+ .owner = THIS_MODULE,
+ .get_id = port_get_id,
+ .enable_set = port_enable_set,
+};
+
static int afu_probe(struct platform_device *pdev)
{
int ret;
@@ -154,7 +253,28 @@ static int afu_remove(struct platform_device *pdev)
.remove = afu_remove,
};

-module_platform_driver(afu_driver);
+static int __init afu_init(void)
+{
+ int ret;
+
+ dfl_fpga_add_port_ops(&afu_port_ops);
+
+ ret = platform_driver_register(&afu_driver);
+ if (ret)
+ dfl_fpga_del_port_ops(&afu_port_ops);
+
+ return ret;
+}
+
+static void __exit afu_exit(void)
+{
+ platform_driver_unregister(&afu_driver);
+
+ dfl_fpga_del_port_ops(&afu_port_ops);
+}
+
+module_init(afu_init);
+module_exit(afu_exit);

MODULE_DESCRIPTION("FPGA Accelerated Function Unit driver");
MODULE_AUTHOR("Intel Corporation");
--
1.8.3.1


2018-05-02 03:05:53

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 22/28] fpga: dfl: fme-region: add support for compat_id

This patch adds compat_id support, it reuses fme manager's
compat id, as the per region compat id is actually from the
fme manager's register.

Signed-off-by: Wu Hao <[email protected]>
---
v5: reuse fme manager's compat_id as per region compat_id
---
drivers/fpga/dfl-fme-region.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
index 696b313..b82a381 100644
--- a/drivers/fpga/dfl-fme-region.c
+++ b/drivers/fpga/dfl-fme-region.c
@@ -45,6 +45,7 @@ static int fme_region_probe(struct platform_device *pdev)
}

region->priv = pdata;
+ region->compat_id = mgr->compat_id;
platform_set_drvdata(pdev, region);

ret = fpga_region_register(region);
--
1.8.3.1


2018-05-02 03:06:05

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 16/28] fpga: dfl: fme: add DFL_FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls support

DFL_FPGA_GET_API_VERSION and DFL_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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
---
v2: switched to GPLv2 license.
v3: rename intel-fpga.h to fpga-dfl.h and rebased.
v4: fix SPDX license issue.
add Acked-by from Alan and Moritz.
v5: rebase due to DFL framework API naming changes.
add DFL_ prefix to IOCTL APIs.
---
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/fpga/dfl-fme-main.c | 12 +++++++++
include/uapi/linux/fpga-dfl.h | 48 ++++++++++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
create mode 100644 include/uapi/linux/fpga-dfl.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 84bb74d..9105755 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -323,6 +323,7 @@ Code Seq#(hex) Include File Comments
0xB3 00 linux/mmc/ioctl.h
0xB4 00-0F linux/gpio.h <mailto:[email protected]>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:[email protected]>
+0xB6 all linux/fpga-dfl.h
0xC0 00-0F linux/usb/iowarrior.h
0xCA 00-0F uapi/misc/cxl.h
0xCA 10-2F uapi/misc/ocxl.h
diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
index 368f1fc..6a59c07 100644
--- a/drivers/fpga/dfl-fme-main.c
+++ b/drivers/fpga/dfl-fme-main.c
@@ -16,6 +16,7 @@

#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/fpga-dfl.h>

#include "dfl.h"

@@ -116,6 +117,13 @@ static ssize_t bitstream_metadata_show(struct device *dev,
},
};

+static long fme_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
+ unsigned long arg)
+{
+ /* No extension support for now */
+ return 0;
+}
+
static int fme_open(struct inode *inode, struct file *filp)
{
struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
@@ -156,6 +164,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) {
+ case DFL_FPGA_GET_API_VERSION:
+ return DFL_FPGA_API_VERSION;
+ case DFL_FPGA_CHECK_EXTENSION:
+ return fme_ioctl_check_extension(pdata, arg);
default:
/*
* Let sub-feature's ioctl function to handle the cmd
diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h
new file mode 100644
index 0000000..7585de0
--- /dev/null
+++ b/include/uapi/linux/fpga-dfl.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Header File for FPGA DFL User API
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Zhang Yi <[email protected]>
+ * Wu Hao <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ */
+
+#ifndef _UAPI_LINUX_FPGA_DFL_H
+#define _UAPI_LINUX_FPGA_DFL_H
+
+#define DFL_FPGA_API_VERSION 0
+
+/*
+ * The IOCTL interface for DFL based FPGA is designed for extensibility by
+ * embedding the structure length (argsz) and flags into structures passed
+ * between kernel and userspace. This design referenced the VFIO IOCTL
+ * interface (include/uapi/linux/vfio.h).
+ */
+
+#define DFL_FPGA_MAGIC 0xB6
+
+#define DFL_FPGA_BASE 0
+
+/**
+ * DFL_FPGA_GET_API_VERSION - _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 0)
+ *
+ * Report the version of the driver API.
+ * Return: Driver API Version.
+ */
+
+#define DFL_FPGA_GET_API_VERSION _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 0)
+
+/**
+ * DFL_FPGA_CHECK_EXTENSION - _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 1)
+ *
+ * Check whether an extension is supported.
+ * Return: 0 if not supported, otherwise the extension is supported.
+ */
+
+#define DFL_FPGA_CHECK_EXTENSION _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 1)
+
+#endif /* _UAPI_LINUX_FPGA_DFL_H */
--
1.8.3.1


2018-05-02 03:06:08

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 25/28] fpga: dfl: afu: add header sub feature support

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>/<dfl-port.x>/id
Read-only. Port ID.

Ioctl interface:
* DFL_FPGA_PORT_RESET
Reset the FPGA Port and its AFU.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
---
v3: rename driver name to fpga-dfl-afu
add more description for reset ioctl.
fix some checkpatch issues.
v4: rebase.
add Acked-by from Alan.
v5: rebase, and merge port functions from DFL framework code.
add DFL_ prefix to APIs.
---
Documentation/ABI/testing/sysfs-platform-dfl-port | 7 ++
drivers/fpga/dfl-afu-main.c | 79 ++++++++++++++++++++++-
include/uapi/linux/fpga-dfl.h | 17 +++++
3 files changed, 102 insertions(+), 1 deletion(-)
create mode 100644 Documentation/ABI/testing/sysfs-platform-dfl-port

diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-port b/Documentation/ABI/testing/sysfs-platform-dfl-port
new file mode 100644
index 0000000..eb1f02d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-dfl-port
@@ -0,0 +1,7 @@
+What: /sys/bus/platform/devices/dfl-port.0/id
+Date: May 2018
+KernelVersion: 4.17
+Contact: Wu Hao <[email protected]>
+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.
diff --git a/drivers/fpga/dfl-afu-main.c b/drivers/fpga/dfl-afu-main.c
index 4bdbe3d..6f846b7 100644
--- a/drivers/fpga/dfl-afu-main.c
+++ b/drivers/fpga/dfl-afu-main.c
@@ -16,6 +16,7 @@

#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/fpga-dfl.h>

#include "dfl.h"

@@ -87,6 +88,41 @@ static int port_disable(struct platform_device *pdev)
return 0;
}

+/*
+ * This function resets the FPGA Port and its accelerator (AFU) by function
+ * __port_disable and __port_enable (set port soft reset bit and then clear
+ * it). Userspace can do Port reset at any time, e.g during DMA or Partial
+ * Reconfiguration. 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.
+ *
+ * Note: the accelerator (AFU) is not accessible when its port is in reset
+ * (disabled). Any attempts on MMIO access to AFU while in reset, will
+ * result errors reported via port error reporting sub feature (if present).
+ */
+static int __port_reset(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = port_disable(pdev);
+ if (!ret)
+ port_enable(pdev);
+
+ return ret;
+}
+
+static int port_reset(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ int ret;
+
+ mutex_lock(&pdata->lock);
+ ret = __port_reset(pdev);
+ mutex_unlock(&pdata->lock);
+
+ return ret;
+}
+
static int port_get_id(struct platform_device *pdev)
{
void __iomem *base;
@@ -96,23 +132,63 @@ static int port_get_id(struct platform_device *pdev)
return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP));
}

+static ssize_t
+id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int id = port_get_id(to_platform_device(dev));
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", id);
+}
+static DEVICE_ATTR_RO(id);
+
+static const struct attribute *port_hdr_attrs[] = {
+ &dev_attr_id.attr,
+ NULL,
+};
+
static int port_hdr_init(struct platform_device *pdev,
struct dfl_feature *feature)
{
dev_dbg(&pdev->dev, "PORT HDR Init.\n");

- return 0;
+ port_reset(pdev);
+
+ return sysfs_create_files(&pdev->dev.kobj, port_hdr_attrs);
}

static void port_hdr_uinit(struct platform_device *pdev,
struct dfl_feature *feature)
{
dev_dbg(&pdev->dev, "PORT HDR UInit.\n");
+
+ sysfs_remove_files(&pdev->dev.kobj, port_hdr_attrs);
+}
+
+static long
+port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
+ unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ switch (cmd) {
+ case DFL_FPGA_PORT_RESET:
+ if (!arg)
+ ret = port_reset(pdev);
+ else
+ ret = -EINVAL;
+ break;
+ default:
+ dev_dbg(&pdev->dev, "%x cmd not handled", cmd);
+ ret = -ENODEV;
+ }
+
+ return ret;
}

static const struct dfl_feature_ops port_hdr_ops = {
.init = port_hdr_init,
.uinit = port_hdr_uinit,
+ .ioctl = port_hdr_ioctl,
};

static struct dfl_feature_driver port_feature_drvs[] = {
@@ -154,6 +230,7 @@ static int afu_release(struct inode *inode, struct file *filp)

pdata = dev_get_platdata(&pdev->dev);

+ port_reset(pdev);
dfl_feature_dev_use_end(pdata);

return 0;
diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h
index 1c8bb2d..777d6ac 100644
--- a/include/uapi/linux/fpga-dfl.h
+++ b/include/uapi/linux/fpga-dfl.h
@@ -28,8 +28,11 @@
#define DFL_FPGA_MAGIC 0xB6

#define DFL_FPGA_BASE 0
+#define DFL_PORT_BASE 0x40
#define DFL_FME_BASE 0x80

+/* Common IOCTLs for both FME and AFU file descriptor */
+
/**
* DFL_FPGA_GET_API_VERSION - _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 0)
*
@@ -48,6 +51,20 @@

#define DFL_FPGA_CHECK_EXTENSION _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 1)

+/* IOCTLs for AFU file descriptor */
+
+/**
+ * DFL_FPGA_PORT_RESET - _IO(DFL_FPGA_MAGIC, DFL_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 DFL_FPGA_PORT_RESET _IO(DFL_FPGA_MAGIC, DFL_PORT_BASE + 0)
+
/* IOCTLs for FME file descriptor */

/**
--
1.8.3.1


2018-05-02 03:06:12

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 19/28] fpga: dfl: fme-mgr: add compat_id support

This patch adds compat_id support to fme manager driver, it
reads the ID from the hardware register. And it could be used
for compatibility check before partial reconfiguration.

Signed-off-by: Wu Hao <[email protected]>
---
drivers/fpga/dfl-fme-mgr.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
index 1c5bc5a..afcdb39 100644
--- a/drivers/fpga/dfl-fme-mgr.c
+++ b/drivers/fpga/dfl-fme-mgr.c
@@ -272,9 +272,17 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
.status = fme_mgr_status,
};

+static void fme_mgr_get_compat_id(void __iomem *fme_pr,
+ struct fpga_compat_id *id)
+{
+ id->id_l = readq(fme_pr + FME_PR_INTFC_ID_L);
+ id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
+}
+
static int fme_mgr_probe(struct platform_device *pdev)
{
struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct fpga_compat_id *compat_id;
struct device *dev = &pdev->dev;
struct fme_mgr_priv *priv;
struct fpga_manager *mgr;
@@ -295,11 +303,18 @@ static int fme_mgr_probe(struct platform_device *pdev)
return PTR_ERR(priv->ioaddr);
}

+ compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
+ if (!compat_id)
+ return -ENOMEM;
+
+ fme_mgr_get_compat_id(priv->ioaddr, compat_id);
+
mgr = fpga_mgr_create(dev, "DFL FME FPGA Manager",
&fme_mgr_ops, priv);
if (!mgr)
return -ENOMEM;

+ mgr->compat_id = compat_id;
platform_set_drvdata(pdev, mgr);

ret = fpga_mgr_register(mgr);
--
1.8.3.1


2018-05-02 03:06:33

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 12/28] fpga: add FPGA DFL PCIe device driver

From: Zhang Yi <[email protected]>

This patch implements the basic framework of the driver for FPGA PCIe
device which implements the Device Feature List (DFL) in its MMIO space.
This driver is verified on Intel(R) PCIe based FPGA DFL devices, including
both integrated (e.g Intel Server Platform with In-package FPGA) and
discrete (e.g Intel FPGA PCIe Acceleration Cards) solutions.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Zhang Yi <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
---
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
v4: rename to FPGA_DFL_PCI (dfl-pci.c) for better reuse.
fix SPDX license issue.
v5: use module_pci_driver() instead.
add Acked-by from Alan and Moritz.
---
drivers/fpga/Kconfig | 15 +++++++
drivers/fpga/Makefile | 3 ++
drivers/fpga/dfl-pci.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 132 insertions(+)
create mode 100644 drivers/fpga/dfl-pci.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 01ad31f..87f3d44 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -140,4 +140,19 @@ config FPGA_DFL
Gate Array (FPGA) solutions which implement Device Feature List.
It provides enumeration APIs, and feature device infrastructure.

+config FPGA_DFL_PCI
+ tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
+ depends on PCI && FPGA_DFL
+ help
+ Select this option to enable PCIe driver for PCIe based
+ Field-Programmable Gate Array (FPGA) solutions which implemented
+ the Device Feature List (DFL). This driver provides interfaces
+ for userspace applications to configure, enumerate, open and access
+ FPGA accelerators on the FPGA DFL devices, enables system level
+ management functions such as FPGA partial reconfiguration, power
+ management, and virtualization with DFL framework and DFL feature
+ device drivers.
+
+ To compile this as a module, choose M here.
+
endif # FPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index c4c62b9..4375630 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -30,3 +30,6 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o

# FPGA Device Feature List Support
obj-$(CONFIG_FPGA_DFL) += dfl.o
+
+# Drivers for FPGAs which implement DFL
+obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c
new file mode 100644
index 0000000..92e1779
--- /dev/null
+++ b/drivers/fpga/dfl-pci.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Device Feature List (DFL) PCIe device
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Zhang Yi <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <linux/aer.h>
+
+#define DRV_VERSION "0.8"
+#define DRV_NAME "dfl-pci"
+
+/* PCI Device ID */
+#define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD
+#define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0
+#define PCIE_DEVICE_ID_PF_DSC_1_X 0x09C4
+/* VF Device */
+#define PCIE_DEVICE_ID_VF_INT_5_X 0xBCBF
+#define PCIE_DEVICE_ID_VF_INT_6_X 0xBCC1
+#define PCIE_DEVICE_ID_VF_DSC_1_X 0x09C5
+
+static struct pci_device_id cci_pcie_id_tbl[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_INT_5_X),},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_INT_5_X),},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_INT_6_X),},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_INT_6_X),},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_PF_DSC_1_X),},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCIE_DEVICE_ID_VF_DSC_1_X),},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl);
+
+static
+int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)
+{
+ int ret;
+
+ ret = pci_enable_device(pcidev);
+ if (ret < 0) {
+ dev_err(&pcidev->dev, "Failed to enable device %d.\n", ret);
+ return ret;
+ }
+
+ ret = pci_enable_pcie_error_reporting(pcidev);
+ if (ret && ret != -EINVAL)
+ dev_info(&pcidev->dev, "PCIE AER unavailable %d.\n", ret);
+
+ ret = pci_request_regions(pcidev, DRV_NAME);
+ if (ret) {
+ dev_err(&pcidev->dev, "Failed to request regions.\n");
+ goto disable_error_report_exit;
+ }
+
+ pci_set_master(pcidev);
+
+ if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(64))) {
+ ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64));
+ if (ret)
+ goto release_region_exit;
+ } else if (!pci_set_dma_mask(pcidev, DMA_BIT_MASK(32))) {
+ ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
+ if (ret)
+ goto release_region_exit;
+ } else {
+ ret = -EIO;
+ dev_err(&pcidev->dev, "No suitable DMA support available.\n");
+ goto release_region_exit;
+ }
+
+ /* TODO: create and add the platform device per feature list */
+ return 0;
+
+release_region_exit:
+ pci_release_regions(pcidev);
+disable_error_report_exit:
+ pci_disable_pcie_error_reporting(pcidev);
+ pci_disable_device(pcidev);
+ return ret;
+}
+
+static void cci_pci_remove(struct pci_dev *pcidev)
+{
+ pci_release_regions(pcidev);
+ pci_disable_pcie_error_reporting(pcidev);
+ pci_disable_device(pcidev);
+}
+
+static struct pci_driver cci_pci_driver = {
+ .name = DRV_NAME,
+ .id_table = cci_pcie_id_tbl,
+ .probe = cci_pci_probe,
+ .remove = cci_pci_remove,
+};
+
+module_pci_driver(cci_pci_driver);
+
+MODULE_DESCRIPTION("FPGA DFL PCIe Device Driver");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
--
1.8.3.1


2018-05-02 03:07:14

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 17/28] fpga: dfl: fme: add partial reconfiguration sub feature support

From: Kang Luwei <[email protected]>

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:
* DFL_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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Kang Luwei <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
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.
v4: rebase and correct comments for some function.
fix SPDX license issue.
remove unnecessary input parameter for destroy_bridge/region function.
add dfl-fme-pr.h for PR sub feature data structure and registers.
v5: rebase due to DFL framework API naming changes.
improve naming for IOCTL API, functions and data structure.
defer finding port platform device to fme bridge.
pass mapped ioaddr to fme manager via pdata.
remove useless devm_kfree().
---
drivers/fpga/Makefile | 2 +-
drivers/fpga/dfl-fme-main.c | 43 +++-
drivers/fpga/dfl-fme-pr.c | 474 ++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/dfl-fme-pr.h | 84 ++++++++
drivers/fpga/dfl-fme.h | 38 ++++
include/uapi/linux/fpga-dfl.h | 28 +++
6 files changed, 667 insertions(+), 2 deletions(-)
create mode 100644 drivers/fpga/dfl-fme-pr.c
create mode 100644 drivers/fpga/dfl-fme-pr.h
create mode 100644 drivers/fpga/dfl-fme.h

diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index fbd1c85..3c44fc9 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -32,7 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
obj-$(CONFIG_FPGA_DFL) += dfl.o
obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o

-dfl-fme-objs := dfl-fme-main.o
+dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o

# Drivers for FPGAs which implement DFL
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
index 6a59c07..bcb9325 100644
--- a/drivers/fpga/dfl-fme-main.c
+++ b/drivers/fpga/dfl-fme-main.c
@@ -19,6 +19,7 @@
#include <linux/fpga-dfl.h>

#include "dfl.h"
+#include "dfl-fme.h"

static ssize_t ports_num_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -113,6 +114,10 @@ static ssize_t bitstream_metadata_show(struct device *dev,
.ops = &fme_hdr_ops,
},
{
+ .id = FME_FEATURE_ID_PR_MGMT,
+ .ops = &pr_mgmt_ops,
+ },
+ {
.ops = NULL,
},
};
@@ -187,6 +192,35 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return -EINVAL;
}

+static int fme_dev_init(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_fme *fme;
+
+ fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL);
+ if (!fme)
+ return -ENOMEM;
+
+ fme->pdata = pdata;
+
+ mutex_lock(&pdata->lock);
+ dfl_fpga_pdata_set_private(pdata, fme);
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+}
+
+static void fme_dev_destroy(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_fme *fme;
+
+ mutex_lock(&pdata->lock);
+ fme = dfl_fpga_pdata_get_private(pdata);
+ dfl_fpga_pdata_set_private(pdata, NULL);
+ mutex_unlock(&pdata->lock);
+}
+
static const struct file_operations fme_fops = {
.owner = THIS_MODULE,
.open = fme_open,
@@ -198,10 +232,14 @@ static int fme_probe(struct platform_device *pdev)
{
int ret;

- ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs);
+ ret = fme_dev_init(pdev);
if (ret)
goto exit;

+ ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs);
+ if (ret)
+ goto dev_destroy;
+
ret = dfl_fpga_register_dev_ops(pdev, &fme_fops, THIS_MODULE);
if (ret)
goto feature_uinit;
@@ -210,6 +248,8 @@ static int fme_probe(struct platform_device *pdev)

feature_uinit:
dfl_fpga_dev_feature_uinit(pdev);
+dev_destroy:
+ fme_dev_destroy(pdev);
exit:
return ret;
}
@@ -218,6 +258,7 @@ static int fme_remove(struct platform_device *pdev)
{
dfl_fpga_unregister_dev_ops(pdev);
dfl_fpga_dev_feature_uinit(pdev);
+ fme_dev_destroy(pdev);

return 0;
}
diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c
new file mode 100644
index 0000000..3d5f8b9
--- /dev/null
+++ b/drivers/fpga/dfl-fme-pr.c
@@ -0,0 +1,474 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Management Engine (FME) Partial Reconfiguration
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Wu Hao <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Christopher Rauer <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#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 "dfl.h"
+#include "dfl-fme.h"
+#include "dfl-fme-pr.h"
+
+static struct dfl_fme_region *
+find_fme_region_by_port_id(struct dfl_fme *fme, int port_id)
+{
+ struct dfl_fme_region *fme_region;
+
+ list_for_each_entry(fme_region, &fme->region_list, node)
+ if (fme_region->port_id == port_id)
+ return fme_region;
+
+ return NULL;
+}
+
+static int dfl_fme_region_match(struct device *dev, const void *data)
+{
+ return dev->parent == data;
+}
+
+static struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id)
+{
+ struct dfl_fme_region *fme_region;
+ struct fpga_region *region;
+
+ fme_region = find_fme_region_by_port_id(fme, port_id);
+ if (!fme_region)
+ return NULL;
+
+ region = fpga_region_class_find(NULL, &fme_region->region->dev,
+ dfl_fme_region_match);
+ if (!region)
+ return NULL;
+
+ return region;
+}
+
+static int fme_pr(struct platform_device *pdev, unsigned long arg)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ void __user *argp = (void __user *)arg;
+ struct dfl_fpga_fme_port_pr port_pr;
+ struct fpga_image_info *info;
+ struct fpga_region *region;
+ void __iomem *fme_hdr;
+ struct dfl_fme *fme;
+ unsigned long minsz;
+ void *buf = NULL;
+ int ret = 0;
+ u64 v;
+
+ minsz = offsetofend(struct dfl_fpga_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 = dfl_get_feature_ioaddr_by_id(&pdev->dev,
+ FME_FEATURE_ID_HEADER);
+
+ /* 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 *)(unsigned long)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 *)(unsigned long)port_pr.buffer_address,
+ port_pr.buffer_size)) {
+ ret = -EFAULT;
+ goto free_exit;
+ }
+
+ /* prepare fpga_image_info for PR */
+ info = fpga_image_info_alloc(&pdev->dev);
+ if (!info) {
+ ret = -ENOMEM;
+ goto free_exit;
+ }
+
+ info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
+
+ mutex_lock(&pdata->lock);
+ fme = dfl_fpga_pdata_get_private(pdata);
+ /* fme device has been unregistered. */
+ if (!fme) {
+ ret = -EINVAL;
+ goto unlock_exit;
+ }
+
+ region = dfl_fme_region_find(fme, port_pr.port_id);
+ if (!region) {
+ ret = -EINVAL;
+ goto unlock_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(&region->bridge_list);
+
+ put_device(&region->dev);
+unlock_exit:
+ mutex_unlock(&pdata->lock);
+free_exit:
+ vfree(buf);
+ if (copy_to_user((void __user *)arg, &port_pr, minsz))
+ return -EFAULT;
+
+ return ret;
+}
+
+/**
+ * dfl_fme_create_mgr - create fpga mgr platform device as child device
+ *
+ * @pdata: fme platform_device's pdata
+ *
+ * Return: mgr platform device if successful, and error code otherwise.
+ */
+static struct platform_device *
+dfl_fme_create_mgr(struct dfl_feature_platform_data *pdata,
+ struct dfl_feature *feature)
+{
+ struct platform_device *mgr, *fme = pdata->dev;
+ struct dfl_fme_mgr_pdata mgr_pdata;
+ int ret = -ENOMEM;
+
+ if (!feature->ioaddr)
+ return ERR_PTR(-ENODEV);
+
+ mgr_pdata.ioaddr = feature->ioaddr;
+
+ /*
+ * Each FME has only one fpga-mgr, so allocate platform device using
+ * the same FME platform device id.
+ */
+ mgr = platform_device_alloc(DFL_FPGA_FME_MGR, fme->id);
+ if (!mgr)
+ return ERR_PTR(ret);
+
+ mgr->dev.parent = &fme->dev;
+
+ ret = platform_device_add_data(mgr, &mgr_pdata, sizeof(mgr_pdata));
+ if (ret)
+ goto create_mgr_err;
+
+ ret = platform_device_add(mgr);
+ if (ret)
+ goto create_mgr_err;
+
+ return mgr;
+
+create_mgr_err:
+ platform_device_put(mgr);
+ return ERR_PTR(ret);
+}
+
+/**
+ * dfl_fme_destroy_mgr - destroy fpga mgr platform device
+ * @pdata: fme platform device's pdata
+ */
+static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata)
+{
+ struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
+
+ platform_device_unregister(priv->mgr);
+}
+
+/**
+ * dfl_fme_create_bridge - create fme fpga bridge platform device as child
+ *
+ * @pdata: fme platform device's pdata
+ * @port_id: port id for the bridge to be created.
+ *
+ * Return: bridge platform device if successful, and error code otherwise.
+ */
+static struct dfl_fme_bridge *
+dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id)
+{
+ struct device *dev = &pdata->dev->dev;
+ struct dfl_fme_br_pdata br_pdata;
+ struct dfl_fme_bridge *fme_br;
+ int ret = -ENOMEM;
+
+ fme_br = devm_kzalloc(dev, sizeof(*fme_br), GFP_KERNEL);
+ if (!fme_br)
+ return ERR_PTR(ret);
+
+ br_pdata.cdev = pdata->dfl_cdev;
+ br_pdata.port_id = port_id;
+
+ fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE,
+ PLATFORM_DEVID_AUTO);
+ if (!fme_br->br)
+ return ERR_PTR(ret);
+
+ fme_br->br->dev.parent = dev;
+
+ ret = platform_device_add_data(fme_br->br, &br_pdata, sizeof(br_pdata));
+ if (ret)
+ goto create_br_err;
+
+ ret = platform_device_add(fme_br->br);
+ if (ret)
+ goto create_br_err;
+
+ return fme_br;
+
+create_br_err:
+ platform_device_put(fme_br->br);
+ return ERR_PTR(ret);
+}
+
+/**
+ * dfl_fme_destroy_bridge - destroy fpga bridge platform device
+ * @fme_br: fme bridge to destroy
+ */
+static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br)
+{
+ platform_device_unregister(fme_br->br);
+}
+
+/**
+ * dfl_fme_destroy_bridge - destroy all fpga bridge platform device
+ * @pdata: fme platform device's pdata
+ */
+static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata)
+{
+ struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
+ struct dfl_fme_bridge *fbridge, *tmp;
+
+ list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) {
+ list_del(&fbridge->node);
+ dfl_fme_destroy_bridge(fbridge);
+ }
+}
+
+/**
+ * dfl_fme_create_region - create fpga region platform device as child
+ *
+ * @pdata: fme platform device's pdata
+ * @mgr: mgr platform device needed for region
+ * @br: br platform device needed for region
+ * @port_id: port id
+ *
+ * Return: fme region if successful, and error code otherwise.
+ */
+static struct dfl_fme_region *
+dfl_fme_create_region(struct dfl_feature_platform_data *pdata,
+ struct platform_device *mgr,
+ struct platform_device *br, int port_id)
+{
+ struct dfl_fme_region_pdata region_pdata;
+ struct device *dev = &pdata->dev->dev;
+ struct dfl_fme_region *fme_region;
+ int ret = -ENOMEM;
+
+ fme_region = devm_kzalloc(dev, sizeof(*fme_region), GFP_KERNEL);
+ if (!fme_region)
+ return ERR_PTR(ret);
+
+ region_pdata.mgr = mgr;
+ region_pdata.br = br;
+
+ /*
+ * Each FPGA device may have more than one port, so allocate platform
+ * device using the same port platform device id.
+ */
+ fme_region->region = platform_device_alloc(DFL_FPGA_FME_REGION, br->id);
+ if (!fme_region->region)
+ return ERR_PTR(ret);
+
+ fme_region->region->dev.parent = dev;
+
+ ret = platform_device_add_data(fme_region->region, &region_pdata,
+ sizeof(region_pdata));
+ if (ret)
+ goto create_region_err;
+
+ ret = platform_device_add(fme_region->region);
+ if (ret)
+ goto create_region_err;
+
+ fme_region->port_id = port_id;
+
+ return fme_region;
+
+create_region_err:
+ platform_device_put(fme_region->region);
+ return ERR_PTR(ret);
+}
+
+/**
+ * dfl_fme_destroy_region - destroy fme region
+ * @fme_region: fme region to destroy
+ */
+static void dfl_fme_destroy_region(struct dfl_fme_region *fme_region)
+{
+ platform_device_unregister(fme_region->region);
+}
+
+/**
+ * dfl_fme_destroy_regions - destroy all fme regions
+ * @pdata: fme platform device's pdata
+ */
+static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata)
+{
+ struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
+ struct dfl_fme_region *fme_region, *tmp;
+
+ list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) {
+ list_del(&fme_region->node);
+ dfl_fme_destroy_region(fme_region);
+ }
+}
+
+static int
+pr_mgmt_init(struct platform_device *pdev, struct dfl_feature *feature)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_fme_region *fme_region;
+ struct dfl_fme_bridge *fme_br;
+ struct platform_device *mgr;
+ struct dfl_fme *priv;
+ void __iomem *fme_hdr;
+ int ret = -ENODEV, i = 0;
+ u64 fme_cap, port_offset;
+
+ fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev,
+ FME_FEATURE_ID_HEADER);
+
+ mutex_lock(&pdata->lock);
+ priv = dfl_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 = dfl_fme_create_mgr(pdata, feature);
+ if (IS_ERR(mgr)) {
+ dev_err(&pdev->dev, "fail to create fpga mgr pdev\n");
+ goto unlock;
+ }
+
+ 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 = dfl_fme_create_bridge(pdata, i);
+ if (IS_ERR(fme_br)) {
+ ret = PTR_ERR(fme_br);
+ goto destroy_region;
+ }
+
+ list_add(&fme_br->node, &priv->bridge_list);
+
+ /* Create region for each port */
+ fme_region = dfl_fme_create_region(pdata, mgr,
+ fme_br->br, i);
+ if (!fme_region) {
+ ret = PTR_ERR(fme_region);
+ goto destroy_region;
+ }
+
+ list_add(&fme_region->node, &priv->region_list);
+ }
+ mutex_unlock(&pdata->lock);
+
+ return 0;
+
+destroy_region:
+ dfl_fme_destroy_regions(pdata);
+ dfl_fme_destroy_bridges(pdata);
+ dfl_fme_destroy_mgr(pdata);
+unlock:
+ mutex_unlock(&pdata->lock);
+ return ret;
+}
+
+static void pr_mgmt_uinit(struct platform_device *pdev,
+ struct dfl_feature *feature)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct dfl_fme *priv;
+
+ mutex_lock(&pdata->lock);
+ priv = dfl_fpga_pdata_get_private(pdata);
+
+ dfl_fme_destroy_regions(pdata);
+ dfl_fme_destroy_bridges(pdata);
+ dfl_fme_destroy_mgr(pdata);
+ mutex_unlock(&pdata->lock);
+}
+
+static long fme_pr_ioctl(struct platform_device *pdev,
+ struct dfl_feature *feature,
+ unsigned int cmd, unsigned long arg)
+{
+ long ret;
+
+ switch (cmd) {
+ case DFL_FPGA_FME_PORT_PR:
+ ret = fme_pr(pdev, arg);
+ break;
+ default:
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+const struct dfl_feature_ops pr_mgmt_ops = {
+ .init = pr_mgmt_init,
+ .uinit = pr_mgmt_uinit,
+ .ioctl = fme_pr_ioctl,
+};
diff --git a/drivers/fpga/dfl-fme-pr.h b/drivers/fpga/dfl-fme-pr.h
new file mode 100644
index 0000000..2e60b53
--- /dev/null
+++ b/drivers/fpga/dfl-fme-pr.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for FPGA Management Engine (FME) Partial Reconfiguration Driver
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Wu Hao <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#ifndef __DFL_FME_PR_H
+#define __DFL_FME_PR_H
+
+#include <linux/platform_device.h>
+
+/**
+ * struct dfl_fme_region - FME fpga region data structure
+ *
+ * @region: platform device of the FPGA region.
+ * @node: used to link fme_region to a list.
+ * @port_id: indicate which port this region connected to.
+ */
+struct dfl_fme_region {
+ struct platform_device *region;
+ struct list_head node;
+ int port_id;
+};
+
+/**
+ * struct dfl_fme_region_pdata - platform data for FME region platform device.
+ *
+ * @mgr: platform device of the FPGA manager.
+ * @br: platform device of the FPGA bridge.
+ * @region_id: region id (same as port_id).
+ */
+struct dfl_fme_region_pdata {
+ struct platform_device *mgr;
+ struct platform_device *br;
+ int region_id;
+};
+
+/**
+ * struct dfl_fme_bridge - FME fpga bridge data structure
+ *
+ * @br: platform device of the FPGA bridge.
+ * @node: used to link fme_bridge to a list.
+ */
+struct dfl_fme_bridge {
+ struct platform_device *br;
+ struct list_head node;
+};
+
+/**
+ * struct dfl_fme_bridge_pdata - platform data for FME bridge platform device.
+ *
+ * @cdev: container device.
+ * @port_id: port id.
+ */
+struct dfl_fme_br_pdata {
+ struct dfl_fpga_cdev *cdev;
+ int port_id;
+};
+
+/**
+ * struct dfl_fme_mgr_pdata - platform data for FME manager platform device.
+ *
+ * @ioaddr: mapped io address for FME manager platform device.
+ */
+struct dfl_fme_mgr_pdata {
+ void __iomem *ioaddr;
+};
+
+#define DFL_FPGA_FME_MGR "dfl-fme-mgr"
+#define DFL_FPGA_FME_BRIDGE "dfl-fme-bridge"
+#define DFL_FPGA_FME_REGION "dfl-fme-region"
+
+#endif /* __DFL_FME_PR_H */
diff --git a/drivers/fpga/dfl-fme.h b/drivers/fpga/dfl-fme.h
new file mode 100644
index 0000000..73b864d
--- /dev/null
+++ b/drivers/fpga/dfl-fme.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for FPGA Management Engine (FME) Driver
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Wu Hao <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#ifndef __DFL_FME_H
+#define __DFL_FME_H
+
+/**
+ * struct dfl_fme - dfl fme private data
+ *
+ * @mgr: FME's FPGA manager platform device.
+ * @region_list: link list of FME's FPGA regions.
+ * @bridge_list: link list of FME's FPGA bridges.
+ * @pdata: fme platform device's pdata.
+ */
+struct dfl_fme {
+ struct platform_device *mgr;
+ struct list_head region_list;
+ struct list_head bridge_list;
+ struct dfl_feature_platform_data *pdata;
+};
+
+extern const struct dfl_feature_ops pr_mgmt_ops;
+
+#endif /* __DFL_FME_H */
diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h
index 7585de0..1c8bb2d 100644
--- a/include/uapi/linux/fpga-dfl.h
+++ b/include/uapi/linux/fpga-dfl.h
@@ -14,6 +14,8 @@
#ifndef _UAPI_LINUX_FPGA_DFL_H
#define _UAPI_LINUX_FPGA_DFL_H

+#include <linux/types.h>
+
#define DFL_FPGA_API_VERSION 0

/*
@@ -26,6 +28,7 @@
#define DFL_FPGA_MAGIC 0xB6

#define DFL_FPGA_BASE 0
+#define DFL_FME_BASE 0x80

/**
* DFL_FPGA_GET_API_VERSION - _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 0)
@@ -45,4 +48,29 @@

#define DFL_FPGA_CHECK_EXTENSION _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 1)

+/* IOCTLs for FME file descriptor */
+
+/**
+ * DFL_FPGA_FME_PORT_PR - _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 0,
+ * struct dfl_fpga_fme_port_pr)
+ *
+ * Driver does Partial Reconfiguration based on Port ID and Buffer (Image)
+ * provided by caller.
+ * Return: 0 on success, -errno on failure.
+ * If DFL_FPGA_FME_PORT_PR returns -EIO, that indicates the HW has detected
+ * some errors during PR, under this case, the user can fetch HW error info
+ * from the status of FME's fpga manager.
+ */
+
+struct dfl_fpga_fme_port_pr {
+ /* Input */
+ __u32 argsz; /* Structure length */
+ __u32 flags; /* Zero for now */
+ __u32 port_id;
+ __u32 buffer_size;
+ __u64 buffer_address; /* Userspace address to the buffer for PR */
+};
+
+#define DFL_FPGA_FME_PORT_PR _IO(DFL_FPGA_MAGIC, DFL_FME_BASE + 0)
+
#endif /* _UAPI_LINUX_FPGA_DFL_H */
--
1.8.3.1


2018-05-02 03:07:26

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 13/28] fpga: dfl-pci: add enumeration for feature devices

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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Zhang Yi <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
---
v3: split from another patch
use common functions from DFL framework for enumeration.
v4: rebase
v5: rebase due to naming changes on DFL framework APIs.
add acked-by from Alan.
---
drivers/fpga/dfl-pci.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 198 insertions(+), 2 deletions(-)

diff --git a/drivers/fpga/dfl-pci.c b/drivers/fpga/dfl-pci.c
index 92e1779..994f09a 100644
--- a/drivers/fpga/dfl-pci.c
+++ b/drivers/fpga/dfl-pci.c
@@ -22,9 +22,52 @@
#include <linux/errno.h>
#include <linux/aer.h>

+#include "dfl.h"
+
#define DRV_VERSION "0.8"
#define DRV_NAME "dfl-pci"

+struct cci_drvdata {
+ struct dfl_fpga_cdev *cdev; /* container device */
+ struct list_head regions; /* list of pci bar mapping region */
+};
+
+/* pci bar mapping info */
+struct cci_region {
+ int bar;
+ void __iomem *ioaddr; /* pointer to mapped bar region */
+ struct list_head node;
+};
+
+static void __iomem *cci_pci_ioremap_bar(struct pci_dev *pcidev, int bar)
+{
+ struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+ struct cci_region *region;
+
+ list_for_each_entry(region, &drvdata->regions, node)
+ if (region->bar == bar) {
+ dev_dbg(&pcidev->dev, "BAR %d region exists\n", bar);
+ return region->ioaddr;
+ }
+
+ region = devm_kzalloc(&pcidev->dev, sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return NULL;
+
+ 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);
+ return NULL;
+ }
+
+ list_add(&region->node, &drvdata->regions);
+
+ return region->ioaddr;
+}
+
/* PCI Device ID */
#define PCIE_DEVICE_ID_PF_INT_5_X 0xBCBD
#define PCIE_DEVICE_ID_PF_INT_6_X 0xBCC0
@@ -45,6 +88,144 @@
};
MODULE_DEVICE_TABLE(pci, cci_pcie_id_tbl);

+static int cci_init_drvdata(struct pci_dev *pcidev)
+{
+ struct cci_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);
+
+ return 0;
+}
+
+static void cci_pci_release_regions(struct pci_dev *pcidev)
+{
+ struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+ struct cci_region *tmp, *region;
+
+ list_for_each_entry_safe(region, tmp, &drvdata->regions, node) {
+ list_del(&region->node);
+ if (region->ioaddr)
+ pci_iounmap(pcidev, region->ioaddr);
+ devm_kfree(&pcidev->dev, region);
+ }
+}
+
+static void cci_remove_drvdata(struct pci_dev *pcidev)
+{
+ struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+
+ cci_pci_release_regions(pcidev);
+ pci_set_drvdata(pcidev, NULL);
+ devm_kfree(&pcidev->dev, drvdata);
+}
+
+static void cci_remove_feature_devs(struct pci_dev *pcidev)
+{
+ struct cci_drvdata *drvdata = pci_get_drvdata(pcidev);
+
+ /* remove all children feature devices */
+ dfl_fpga_remove_feature_devs(drvdata->cdev);
+}
+
+/* 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 dfl_fpga_enum_info *info;
+ struct dfl_fpga_cdev *cdev;
+ resource_size_t start, len;
+ int port_num, bar, i, ret = 0;
+ void __iomem *base;
+ u32 offset;
+ u64 v;
+
+ /* allocate enumeration info via pci_dev */
+ info = dfl_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 (dfl_feature_is_fme(base)) {
+ start = pci_resource_start(pcidev, 0);
+ len = pci_resource_len(pcidev, 0);
+
+ dfl_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_DFL_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;
+
+ dfl_fpga_enum_info_add_dfl(info, start, len,
+ base + offset);
+ }
+ } else if (dfl_feature_is_port(base)) {
+ start = pci_resource_start(pcidev, 0);
+ len = pci_resource_len(pcidev, 0);
+
+ dfl_fpga_enum_info_add_dfl(info, start, len, base);
+ } else {
+ ret = -ENODEV;
+ goto enum_info_free_exit;
+ }
+
+ /* start enumeration with prepared enumeration information */
+ cdev = dfl_fpga_enumerate_feature_devs(info);
+ if (IS_ERR(cdev)) {
+ dev_err(&pcidev->dev, "Enumeration failure\n");
+ ret = PTR_ERR(cdev);
+ goto enum_info_free_exit;
+ }
+
+ drvdata->cdev = cdev;
+
+enum_info_free_exit:
+ dfl_fpga_enum_info_free(info);
+
+ return ret;
+}
+
static
int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)
{
@@ -82,9 +263,22 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)
goto release_region_exit;
}

- /* TODO: create and add the platform device per feature list */
- return 0;
+ ret = cci_init_drvdata(pcidev);
+ if (ret) {
+ dev_err(&pcidev->dev, "Fail to init drvdata %d.\n", ret);
+ goto release_region_exit;
+ }
+
+ ret = cci_enumerate_feature_devs(pcidev);
+ if (ret) {
+ dev_err(&pcidev->dev, "enumeration failure %d.\n", ret);
+ goto remove_drvdata_exit;
+ }
+
+ return ret;

+remove_drvdata_exit:
+ cci_remove_drvdata(pcidev);
release_region_exit:
pci_release_regions(pcidev);
disable_error_report_exit:
@@ -95,6 +289,8 @@ int cci_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *pcidevid)

static void cci_pci_remove(struct pci_dev *pcidev)
{
+ cci_remove_feature_devs(pcidev);
+ cci_remove_drvdata(pcidev);
pci_release_regions(pcidev);
pci_disable_pcie_error_reporting(pcidev);
pci_disable_device(pcidev);
--
1.8.3.1


2018-05-02 03:07:36

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 14/28] fpga: dfl: add FPGA Management Engine driver basic framework

From: Kang Luwei <[email protected]>

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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Kang Luwei <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
v3: rename driver from intel-fpga-fme to dfl-fme
rename Kconfig from INTEL_FPGA_FME to FPGA_DFL_FME
v4: fix SPDX license issue, use dfl-fme as module name
v5: rebase, due to DFL framework naming changes on functions and data structures.
fix uinit order in remove function.
remove else block in fme_ioctl function per suggestion from Alan.
---
drivers/fpga/Kconfig | 10 +++
drivers/fpga/Makefile | 3 +
drivers/fpga/dfl-fme-main.c | 158 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 171 insertions(+)
create mode 100644 drivers/fpga/dfl-fme-main.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 87f3d44..103d5e2 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -140,6 +140,16 @@ config FPGA_DFL
Gate Array (FPGA) solutions which implement Device Feature List.
It provides enumeration APIs, and feature device infrastructure.

+config FPGA_DFL_FME
+ tristate "FPGA DFL FME Driver"
+ depends on FPGA_DFL
+ help
+ The FPGA Management Engine (FME) is a feature device implemented
+ under Device Feature List (DFL) framework. Select this option to
+ enable the platform device driver for FME which implements all
+ FPGA platform level management features. There shall be 1 FME
+ per DFL based FPGA device.
+
config FPGA_DFL_PCI
tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
depends on PCI && FPGA_DFL
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 4375630..fbd1c85 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -30,6 +30,9 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o

# FPGA Device Feature List Support
obj-$(CONFIG_FPGA_DFL) += dfl.o
+obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
+
+dfl-fme-objs := dfl-fme-main.o

# Drivers for FPGAs which implement DFL
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
new file mode 100644
index 0000000..000651f
--- /dev/null
+++ b/drivers/fpga/dfl-fme-main.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Management Engine (FME)
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ * Joseph Grecco <[email protected]>
+ * Enno Luebbers <[email protected]>
+ * Tim Whisonant <[email protected]>
+ * Ananda Ravuri <[email protected]>
+ * Henry Mitchel <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "dfl.h"
+
+static int
+fme_hdr_init(struct platform_device *pdev, struct dfl_feature *feature)
+{
+ dev_dbg(&pdev->dev, "FME HDR Init.\n");
+
+ return 0;
+}
+
+static void
+fme_hdr_uinit(struct platform_device *pdev, struct dfl_feature *feature)
+{
+ dev_dbg(&pdev->dev, "FME HDR UInit.\n");
+}
+
+static const struct dfl_feature_ops fme_hdr_ops = {
+ .init = fme_hdr_init,
+ .uinit = fme_hdr_uinit,
+};
+
+static struct dfl_feature_driver fme_feature_drvs[] = {
+ {
+ .id = FME_FEATURE_ID_HEADER,
+ .ops = &fme_hdr_ops,
+ },
+ {
+ .ops = NULL,
+ },
+};
+
+static int fme_open(struct inode *inode, struct file *filp)
+{
+ struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev);
+ int ret;
+
+ if (WARN_ON(!pdata))
+ return -ENODEV;
+
+ ret = dfl_feature_dev_use_begin(pdata);
+ if (ret)
+ return ret;
+
+ dev_dbg(&fdev->dev, "Device File Open\n");
+ filp->private_data = pdata;
+
+ return 0;
+}
+
+static int fme_release(struct inode *inode, struct file *filp)
+{
+ struct dfl_feature_platform_data *pdata = filp->private_data;
+ struct platform_device *pdev = pdata->dev;
+
+ dev_dbg(&pdev->dev, "Device File Release\n");
+ dfl_feature_dev_use_end(pdata);
+
+ return 0;
+}
+
+static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct dfl_feature_platform_data *pdata = filp->private_data;
+ struct platform_device *pdev = pdata->dev;
+ struct dfl_feature *f;
+ long ret;
+
+ dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
+
+ switch (cmd) {
+ default:
+ /*
+ * Let sub-feature's ioctl function to handle the cmd
+ * Sub-feature's ioctl returns -ENODEV when cmd is not
+ * handled in this sub feature, and returns 0 and other
+ * error code if cmd is handled.
+ */
+ dfl_fpga_dev_for_each_feature(pdata, f) {
+ if (f->ops && f->ops->ioctl) {
+ ret = f->ops->ioctl(pdev, f, cmd, arg);
+ if (ret != -ENODEV)
+ return ret;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct file_operations fme_fops = {
+ .owner = THIS_MODULE,
+ .open = fme_open,
+ .release = fme_release,
+ .unlocked_ioctl = fme_ioctl,
+};
+
+static int fme_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs);
+ if (ret)
+ goto exit;
+
+ ret = dfl_fpga_register_dev_ops(pdev, &fme_fops, THIS_MODULE);
+ if (ret)
+ goto feature_uinit;
+
+ return 0;
+
+feature_uinit:
+ dfl_fpga_dev_feature_uinit(pdev);
+exit:
+ return ret;
+}
+
+static int fme_remove(struct platform_device *pdev)
+{
+ dfl_fpga_unregister_dev_ops(pdev);
+ dfl_fpga_dev_feature_uinit(pdev);
+
+ return 0;
+}
+
+static struct platform_driver fme_driver = {
+ .driver = {
+ .name = DFL_FPGA_FEATURE_DEV_FME,
+ },
+ .probe = fme_probe,
+ .remove = fme_remove,
+};
+
+module_platform_driver(fme_driver);
+
+MODULE_DESCRIPTION("FPGA Management Engine driver");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dfl-fme");
--
1.8.3.1


2018-05-02 03:08:18

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 15/28] fpga: dfl: fme: add header sub feature support

From: Kang Luwei <[email protected]>

The Header Register set is always present for FPGA Management Engine (FME),
this patch implements init and uinit function for header sub feature and
introduces several read-only sysfs interfaces for the capability and
status.

Sysfs interfaces:
* /sys/class/fpga_region/<regionX>/<dfl-fme.x>/ports_num
Read-only. Number of ports implemented

* /sys/class/fpga_region/<regionX>/<dfl-fme.x>/bitstream_id
Read-only. Bitstream (static FPGA region) identifier number. It contains
the detailed version and other information of this static FPGA region.

* /sys/class/fpga_region/<regionX>/<dfl-fme.x>/bitstream_metadata
Read-only. Bitstream (static FPGA region) meta data. It contains the
synthesis date, seed and other information of this static FPGA region.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Kang Luwei <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
---
v2: add sysfs documentation
v3: rename driver to fpga-dfl-fme.
improve sysfs doc and commit description.
replace bitfield.
v4: rebase and switch to use id for sub feature matching.
add more description for bitstream_id/metadata.
v5: rebase due to DFL framework API naming changes.
replace "blue bitstream" terminology.
fix one typo in sysfs doc and add acked-by from Alan.
---
Documentation/ABI/testing/sysfs-platform-dfl-fme | 23 ++++++++
drivers/fpga/dfl-fme-main.c | 68 ++++++++++++++++++++++++
2 files changed, 91 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-platform-dfl-fme

diff --git a/Documentation/ABI/testing/sysfs-platform-dfl-fme b/Documentation/ABI/testing/sysfs-platform-dfl-fme
new file mode 100644
index 0000000..2dbfba7
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-dfl-fme
@@ -0,0 +1,23 @@
+What: /sys/bus/platform/devices/dfl-fme.0/ports_num
+Date: May 2018
+KernelVersion: 4.18
+Contact: Wu Hao <[email protected]>
+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/dfl-fme.0/bitstream_id
+Date: May 2018
+KernelVersion: 4.18
+Contact: Wu Hao <[email protected]>
+Description: Read-only. It returns Bitstream (static FPGA region)
+ identifier number, which includes the detailed version
+ and other information of this static FPGA region.
+
+What: /sys/bus/platform/devices/dfl-fme.0/bitstream_metadata
+Date: May 2018
+KernelVersion: 4.18
+Contact: Wu Hao <[email protected]>
+Description: Read-only. It returns Bitstream (static FPGA region) meta
+ data, which includes the synthesis date, seed and other
+ information of this static FPGA region.
diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
index 000651f..368f1fc 100644
--- a/drivers/fpga/dfl-fme-main.c
+++ b/drivers/fpga/dfl-fme-main.c
@@ -19,10 +19,77 @@

#include "dfl.h"

+static ssize_t ports_num_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ void __iomem *base;
+ u64 v;
+
+ base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
+
+ v = readq(base + FME_HDR_CAP);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ (unsigned int)FIELD_GET(FME_CAP_NUM_PORTS, v));
+}
+static DEVICE_ATTR_RO(ports_num);
+
+/*
+ * Bitstream (static FPGA region) identifier number. It contains the
+ * detailed version and other information of this static FPGA region.
+ */
+static ssize_t bitstream_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ void __iomem *base;
+ u64 v;
+
+ base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
+
+ v = readq(base + FME_HDR_BITSTREAM_ID);
+
+ return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v);
+}
+static DEVICE_ATTR_RO(bitstream_id);
+
+/*
+ * Bitstream (static FPGA region) meta data. It contains the synthesis
+ * date, seed and other information of this static FPGA region.
+ */
+static ssize_t bitstream_metadata_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ void __iomem *base;
+ u64 v;
+
+ base = dfl_get_feature_ioaddr_by_id(dev, FME_FEATURE_ID_HEADER);
+
+ v = readq(base + FME_HDR_BITSTREAM_MD);
+
+ return scnprintf(buf, PAGE_SIZE, "0x%llx\n", (unsigned long long)v);
+}
+static DEVICE_ATTR_RO(bitstream_metadata);
+
+static const struct attribute *fme_hdr_attrs[] = {
+ &dev_attr_ports_num.attr,
+ &dev_attr_bitstream_id.attr,
+ &dev_attr_bitstream_metadata.attr,
+ NULL,
+};
+
static int
fme_hdr_init(struct platform_device *pdev, struct dfl_feature *feature)
{
+ void __iomem *base = feature->ioaddr;
+ int ret;
+
dev_dbg(&pdev->dev, "FME HDR Init.\n");
+ dev_dbg(&pdev->dev, "FME cap %llx.\n",
+ (unsigned long long)readq(base + FME_HDR_CAP));
+
+ ret = sysfs_create_files(&pdev->dev.kobj, fme_hdr_attrs);
+ if (ret)
+ return ret;

return 0;
}
@@ -31,6 +98,7 @@
fme_hdr_uinit(struct platform_device *pdev, struct dfl_feature *feature)
{
dev_dbg(&pdev->dev, "FME HDR UInit.\n");
+ sysfs_remove_files(&pdev->dev.kobj, fme_hdr_attrs);
}

static const struct dfl_feature_ops fme_hdr_ops = {
--
1.8.3.1


2018-05-02 03:08:22

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 10/28] fpga: dfl: add dfl_fpga_port_ops support.

In some cases, other DFL driver modules may need to access some port
operations, e.g disable / enable port for partial reconfiguration in
FME module. In order to avoid dependency between port and FME modules,
this patch introduces the dfl_fpga_port_ops support in DFL framework.
A global dfl_fpga_port_ops list is added in the DFL framework, and
it allows other DFL modules to use these port operations registered
to this list, even in virtualization case, the port platform device
is turned into VF / guest VM and hidden in host, the registered
port_ops is still usable. It resolves the dependency issues between
modules, but once get port ops API returns a valid port ops, that
means related port driver module has been module_get to prevent from
unexpected unload, and put port ops API must be invoked after use.

These APIs introduced by this patch is listed below:
* dfl_fpga_add_port_ops
add one port ops to the global list.

* dfl_fpga_del_port_ops
del one port ops from the global list.

* dfl_fpga_get_port_ops / dfl_fpga_put_port_ops
get/put the port ops before/after use.

Signed-off-by: Wu Hao <[email protected]>
---
drivers/fpga/dfl.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/dfl.h | 21 +++++++++++++++++
2 files changed, 88 insertions(+)

diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index c4c47d6..b5a14a4 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -14,6 +14,73 @@

#include "dfl.h"

+static DEFINE_MUTEX(dfl_port_ops_mutex);
+static LIST_HEAD(dfl_port_ops_list);
+
+/**
+ * dfl_fpga_get_port_ops - get matched port ops from the global list
+ * @pdev: platform device to match with associated port ops.
+ * Return: matched port ops on success, NULL otherwise.
+ *
+ * Please note that must dfl_fpga_put_port_ops after use the port_ops.
+ */
+struct dfl_fpga_port_ops *dfl_fpga_get_port_ops(struct platform_device *pdev)
+{
+ struct dfl_fpga_port_ops *ops = NULL;
+
+ mutex_lock(&dfl_port_ops_mutex);
+ if (list_empty(&dfl_port_ops_list))
+ goto done;
+
+ list_for_each_entry(ops, &dfl_port_ops_list, node) {
+ /* match port_ops using the name of platform device */
+ if (!strcmp(pdev->name, ops->name)) {
+ if (!try_module_get(ops->owner))
+ ops = NULL;
+ goto done;
+ }
+ }
+done:
+ mutex_unlock(&dfl_port_ops_mutex);
+ return ops;
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_get_port_ops);
+
+/**
+ * dfl_fpga_put_port_ops - put port ops
+ * @ops: port ops.
+ */
+void dfl_fpga_put_port_ops(struct dfl_fpga_port_ops *ops)
+{
+ if (ops && ops->owner)
+ module_put(ops->owner);
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_put_port_ops);
+
+/**
+ * dfl_fpga_add_port_ops - add port_ops to global list
+ * @ops: port ops to add.
+ */
+void dfl_fpga_add_port_ops(struct dfl_fpga_port_ops *ops)
+{
+ mutex_lock(&dfl_port_ops_mutex);
+ list_add_tail(&ops->node, &dfl_port_ops_list);
+ mutex_unlock(&dfl_port_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_add_port_ops);
+
+/**
+ * dfl_fpga_del_port_ops - remove port_ops from global list
+ * @ops: port ops to del.
+ */
+void dfl_fpga_del_port_ops(struct dfl_fpga_port_ops *ops)
+{
+ mutex_lock(&dfl_port_ops_mutex);
+ list_del(&ops->node);
+ mutex_unlock(&dfl_port_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_del_port_ops);
+
static DEFINE_MUTEX(dfl_id_mutex);

enum dfl_id_type {
diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
index 27f7a74..c9d9a01 100644
--- a/drivers/fpga/dfl.h
+++ b/drivers/fpga/dfl.h
@@ -130,6 +130,27 @@
/* Latency tolerance reporting. '1' >= 40us, '0' < 40us.*/
#define PORT_CTRL_LATENCY BIT_ULL(2)
#define PORT_CTRL_SFTRST_ACK BIT_ULL(4) /* HW ack for reset */
+/**
+ * struct dfl_fpga_port_ops - port ops
+ *
+ * @name: name of this port ops, to match with port platform device.
+ * @owner: pointer to the module which owns this port ops.
+ * @node: node to link port ops to global list.
+ * @get_id: get port id from hardware.
+ * @enable_set: enable/disable the port.
+ */
+struct dfl_fpga_port_ops {
+ const char *name;
+ struct module *owner;
+ struct list_head node;
+ int (*get_id)(struct platform_device *pdev);
+ int (*enable_set)(struct platform_device *pdev, bool enable);
+};
+
+void dfl_fpga_add_port_ops(struct dfl_fpga_port_ops *ops);
+void dfl_fpga_del_port_ops(struct dfl_fpga_port_ops *ops);
+struct dfl_fpga_port_ops *dfl_fpga_get_port_ops(struct platform_device *pdev);
+void dfl_fpga_put_port_ops(struct dfl_fpga_port_ops *ops);

/**
* struct dfl_feature_driver - sub feature's driver
--
1.8.3.1


2018-05-02 03:08:32

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 08/28] fpga: dfl: add dfl_fpga_cdev_find_port

For feature devices, we need a method to find the port dedicated
to the device. This patch adds a function dfl_fpga_cdev_find_port
for this purpose. e.g FPGA Management Engine (FME) Partial
Reconfiguration sub feature, it uses this function to find
dedicated port on the device for PR function implementation.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
---
v3: s/fpga_for_each_port/fpga_cdev_find_port/
move fpga_cdev_find_port to fpga-dfl module.
v4: improve description in commit message.
add comments to remind user to put_device after use this function.
v5: add "dfl_" prefix to functions.
improve function comments per suggestion from Alan Tull.
add Acked-by from Alan and Moritz.
---
drivers/fpga/dfl.c | 32 ++++++++++++++++++++++++++++++++
drivers/fpga/dfl.h | 21 +++++++++++++++++++++
2 files changed, 53 insertions(+)

diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index 18aba02..1e06efb 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -795,6 +795,38 @@ void dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev)
}
EXPORT_SYMBOL_GPL(dfl_fpga_remove_feature_devs);

+/**
+ * __dfl_fpga_cdev_find_port - find a port under given container device
+ *
+ * @cdev: container device
+ * @data: data passed to match function
+ * @match: match function used to find specific port from the port device list
+ *
+ * Find a port device under container device. This function needs to be
+ * invoked with lock held.
+ *
+ * Return: pointer to port's platform device if successful, NULL otherwise.
+ *
+ * NOTE: you will need to drop the device reference with put_device() after use.
+ */
+struct platform_device *
+__dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data,
+ int (*match)(struct platform_device *, void *))
+{
+ struct dfl_feature_platform_data *pdata;
+ struct platform_device *port_dev;
+
+ list_for_each_entry(pdata, &cdev->port_dev_list, node) {
+ port_dev = pdata->dev;
+
+ if (match(port_dev, data) && get_device(&port_dev->dev))
+ return port_dev;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(__dfl_fpga_cdev_find_port);
+
static int __init dfl_fpga_init(void)
{
int ret;
diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
index 5fcb1a1..2b6aaef 100644
--- a/drivers/fpga/dfl.h
+++ b/drivers/fpga/dfl.h
@@ -290,4 +290,25 @@ struct dfl_fpga_cdev *
dfl_fpga_enumerate_feature_devs(struct dfl_fpga_enum_info *info);
void dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev);

+/*
+ * need to drop the device reference with put_device() after use port platform
+ * device returned by __dfl_fpga_cdev_find_port and dfl_fpga_cdev_find_port
+ * functions.
+ */
+struct platform_device *
+__dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data,
+ int (*match)(struct platform_device *, void *));
+
+static inline struct platform_device *
+dfl_fpga_cdev_find_port(struct dfl_fpga_cdev *cdev, void *data,
+ int (*match)(struct platform_device *, void *))
+{
+ struct platform_device *pdev;
+
+ mutex_lock(&cdev->lock);
+ pdev = __dfl_fpga_cdev_find_port(cdev, data, match);
+ mutex_unlock(&cdev->lock);
+
+ return pdev;
+}
#endif /* __FPGA_DFL_H */
--
1.8.3.1


2018-05-02 03:08:42

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 03/28] fpga: mgr: add status for fpga-manager

This patch adds status sysfs interface for fpga manager, it's a
read only interface which allows user to get fpga manager status,
including full/partial reconfiguration error and other status
information. It adds a status callback to fpga_manager_ops too,
allows each fpga_manager driver to define its own method to
collect latest status from hardware.

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 <[email protected]>
Acked-by: Alan Tull <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
---
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/
v4: simply code per suggestion from Alan.
add supported status strings (and descriptions) in sysfs document.
remove unused error code (SECURE_LOAD_ERR).
v5: fix the wrong commit message and add Acked-by from Alan and Moritz.
---
Documentation/ABI/testing/sysfs-class-fpga-manager | 24 +++++++++++++++++++
drivers/fpga/fpga-mgr.c | 28 ++++++++++++++++++++++
include/linux/fpga/fpga-mgr.h | 9 +++++++
3 files changed, 61 insertions(+)

diff --git a/Documentation/ABI/testing/sysfs-class-fpga-manager b/Documentation/ABI/testing/sysfs-class-fpga-manager
index 23056c5..a54450d 100644
--- a/Documentation/ABI/testing/sysfs-class-fpga-manager
+++ b/Documentation/ABI/testing/sysfs-class-fpga-manager
@@ -35,3 +35,27 @@ 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: May 2018
+KernelVersion: 4.17
+Contact: Wu Hao <[email protected]>
+Description: Read fpga manager status as a string.
+ If FPGA programming operation fails, it could be caused by crc
+ error or incompatible bitstream image. The intent of this
+ interface is to provide more detailed information for FPGA
+ programming errors to userspace. This is a list of strings for
+ the supported status.
+
+ * reconfig operation error - invalid operations detected by
+ reconfiguration hardware.
+ e.g start reconfiguration
+ with errors not cleared
+ * reconfig CRC error - CRC error detected by
+ reconfiguration hardware.
+ * reconfig incompatible image - reconfiguration image is
+ incompatible with hardware
+ * reconfig IP protocol error - protocol errors detected by
+ reconfiguration hardware
+ * reconfig fifo overflow error - FIFO overflow detected by
+ reconfiguration hardware
diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c
index 0a5181d..6479fb9 100644
--- a/drivers/fpga/fpga-mgr.c
+++ b/drivers/fpga/fpga-mgr.c
@@ -397,12 +397,40 @@ static ssize_t state_show(struct device *dev,
return sprintf(buf, "%s\n", state_str[mgr->state]);
}

+static ssize_t status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fpga_manager *mgr = to_fpga_manager(dev);
+ u64 status;
+ int len = 0;
+
+ if (!mgr->mops->status)
+ return -ENOENT;
+
+ status = mgr->mops->status(mgr);
+
+ if (status & FPGA_MGR_STATUS_OPERATION_ERR)
+ len += sprintf(buf + len, "reconfig operation error\n");
+ if (status & FPGA_MGR_STATUS_CRC_ERR)
+ len += sprintf(buf + len, "reconfig CRC error\n");
+ if (status & FPGA_MGR_STATUS_INCOMPATIBLE_IMAGE_ERR)
+ len += sprintf(buf + len, "reconfig incompatible image\n");
+ if (status & FPGA_MGR_STATUS_IP_PROTOCOL_ERR)
+ len += sprintf(buf + len, "reconfig IP protocol error\n");
+ if (status & FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR)
+ len += sprintf(buf + len, "reconfig fifo overflow error\n");
+
+ return len;
+}
+
static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(state);
+static DEVICE_ATTR_RO(status);

static struct attribute *fpga_mgr_attrs[] = {
&dev_attr_name.attr,
&dev_attr_state.attr,
+ &dev_attr_status.attr,
NULL,
};
ATTRIBUTE_GROUPS(fpga_mgr);
diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index eecfca5..802eac8 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -112,6 +112,7 @@ struct fpga_image_info {
* struct fpga_manager_ops - ops for low level fpga manager drivers
* @initial_header_size: Maximum number of bytes that should be passed into write_init
* @state: returns an enum value of the FPGA's state
+ * @status: returns status of the FPGA, including reconfiguration error code
* @write_init: prepare the FPGA to receive confuration data
* @write: write count bytes of configuration data to the FPGA
* @write_sg: write the scatter list of configuration data to the FPGA
@@ -126,6 +127,7 @@ struct fpga_image_info {
struct fpga_manager_ops {
size_t initial_header_size;
enum fpga_mgr_states (*state)(struct fpga_manager *mgr);
+ u64 (*status)(struct fpga_manager *mgr);
int (*write_init)(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count);
@@ -137,6 +139,13 @@ struct fpga_manager_ops {
const struct attribute_group **groups;
};

+/* FPGA manager status: Partial/Full Reconfiguration errors */
+#define FPGA_MGR_STATUS_OPERATION_ERR BIT(0)
+#define FPGA_MGR_STATUS_CRC_ERR BIT(1)
+#define FPGA_MGR_STATUS_INCOMPATIBLE_IMAGE_ERR BIT(2)
+#define FPGA_MGR_STATUS_IP_PROTOCOL_ERR BIT(3)
+#define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR BIT(4)
+
/**
* struct fpga_manager - fpga manager structure
* @name: name of low level fpga manager
--
1.8.3.1


2018-05-02 03:08:45

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 07/28] fpga: dfl: add chardev support for feature devices

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 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 <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Zhang Yi <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
v2: rebased
v3: move chardev support to fpga-dfl framework
v4: rebase, and add more comments in code.
v5: rebase, and add dfl_ prefix to APIs and data structures.
---
drivers/fpga/dfl.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
drivers/fpga/dfl.h | 14 ++++++++
2 files changed, 117 insertions(+), 1 deletion(-)

diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index c1462e9..18aba02 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -74,6 +74,96 @@ static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
return DFL_ID_MAX;
}

+struct dfl_chardev_info {
+ const char *name;
+ dev_t devt;
+};
+
+/* indexed by enum dfl_fpga_devt_type */
+struct dfl_chardev_info dfl_chrdevs[] = {
+ {.name = DFL_FPGA_FEATURE_DEV_FME}, /* DFL_FPGA_DEVT_FME */
+ {.name = DFL_FPGA_FEATURE_DEV_PORT}, /* DFL_FPGA_DEVT_AFU */
+};
+
+static void dfl_chardev_uinit(void)
+{
+ int i;
+
+ for (i = 0; i < DFL_FPGA_DEVT_MAX; i++)
+ if (MAJOR(dfl_chrdevs[i].devt)) {
+ unregister_chrdev_region(dfl_chrdevs[i].devt,
+ MINORMASK);
+ dfl_chrdevs[i].devt = MKDEV(0, 0);
+ }
+}
+
+static int dfl_chardev_init(void)
+{
+ int i, ret;
+
+ for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) {
+ ret = alloc_chrdev_region(&dfl_chrdevs[i].devt, 0, MINORMASK,
+ dfl_chrdevs[i].name);
+ if (ret)
+ goto exit;
+ }
+
+ return 0;
+
+exit:
+ dfl_chardev_uinit();
+ return ret;
+}
+
+static dev_t dfl_get_devt(enum dfl_fpga_devt_type type, int id)
+{
+ WARN_ON(type >= DFL_FPGA_DEVT_MAX);
+
+ return MKDEV(MAJOR(dfl_chrdevs[type].devt), id);
+}
+
+/**
+ * dfl_fpga_register_dev_ops - register cdev ops for feature dev
+ *
+ * @pdev: feature dev.
+ * @fops: file operations for feature dev's cdev.
+ * @owner: owning module/driver.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int dfl_fpga_register_dev_ops(struct platform_device *pdev,
+ const struct file_operations *fops,
+ struct module *owner)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ cdev_init(&pdata->cdev, fops);
+ pdata->cdev.owner = owner;
+
+ /*
+ * set parent to the feature device so that its refcount is
+ * decreased after the last refcount of cdev is gone, that
+ * makes sure the feature device is valid during device
+ * file's life-cycle.
+ */
+ pdata->cdev.kobj.parent = &pdev->dev.kobj;
+
+ return cdev_add(&pdata->cdev, pdev->dev.devt, 1);
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_register_dev_ops);
+
+/**
+ * dfl_fpga_unregister_dev_ops - unregister cdev ops for feature dev
+ * @pdev: feature dev.
+ */
+void dfl_fpga_unregister_dev_ops(struct platform_device *pdev)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
+
+ cdev_del(&pdata->cdev);
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_unregister_dev_ops);
+
/**
* struct build_feature_devs_info - info collected during feature dev build.
*
@@ -208,9 +298,13 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
enum dfl_id_type type, const char *name,
void __iomem *ioaddr)
{
+ enum dfl_fpga_devt_type devt_type = DFL_FPGA_DEVT_FME;
struct platform_device *fdev;
int ret;

+ if (type == PORT_ID)
+ devt_type = DFL_FPGA_DEVT_PORT;
+
/* we will create a new device, commit current device first */
ret = build_info_commit_dev(binfo);
if (ret)
@@ -234,6 +328,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
return fdev->id;

fdev->dev.parent = &binfo->cdev->region->dev;
+ fdev->dev.devt = dfl_get_devt(devt_type, fdev->id);

return 0;
}
@@ -702,13 +797,20 @@ void dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev)

static int __init dfl_fpga_init(void)
{
+ int ret;
+
dfl_ids_init();

- return 0;
+ ret = dfl_chardev_init();
+ if (ret)
+ dfl_ids_destroy();
+
+ return ret;
}

static void __exit dfl_fpga_exit(void)
{
+ dfl_chardev_uinit();
dfl_ids_destroy();
}

diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
index 2ede915..5fcb1a1 100644
--- a/drivers/fpga/dfl.h
+++ b/drivers/fpga/dfl.h
@@ -15,6 +15,7 @@
#define __FPGA_DFL_H

#include <linux/bitfield.h>
+#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/iopoll.h>
@@ -150,6 +151,7 @@ struct dfl_feature {
*
* @node: node to link feature devs to container device's port_dev_list.
* @lock: mutex to protect platform data.
+ * @cdev: cdev of feature dev.
* @dev: ptr to platform device linked with this platform data.
* @dfl_cdev: ptr to container device.
* @disable_count: count for port disable.
@@ -159,6 +161,7 @@ struct dfl_feature {
struct dfl_feature_platform_data {
struct list_head node;
struct mutex lock;
+ struct cdev cdev;
struct platform_device *dev;
struct dfl_fpga_cdev *dfl_cdev;
unsigned int disable_count;
@@ -176,6 +179,17 @@ static inline int dfl_feature_platform_data_size(const int num)
num * sizeof(struct dfl_feature);
}

+enum dfl_fpga_devt_type {
+ DFL_FPGA_DEVT_FME,
+ DFL_FPGA_DEVT_PORT,
+ DFL_FPGA_DEVT_MAX,
+};
+
+int dfl_fpga_register_dev_ops(struct platform_device *pdev,
+ const struct file_operations *fops,
+ struct module *owner);
+void dfl_fpga_unregister_dev_ops(struct platform_device *pdev);
+
#define dfl_fpga_dev_for_each_feature(pdata, feature) \
for ((feature) = (pdata)->features; \
(feature) < (pdata)->features + (pdata)->num; (feature)++)
--
1.8.3.1


2018-05-02 03:09:19

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 11/28] fpga: dfl: add dfl_fpga_check_port_id function.

This patch adds one common function in DFL framework. It uses
port_ops get_id callback to get port id and compare it with given
value. This function could be used as match function of the
dfl_fpga_cdev_find_port function.

Signed-off-by: Wu Hao <[email protected]>
---
drivers/fpga/dfl.c | 22 ++++++++++++++++++++++
drivers/fpga/dfl.h | 1 +
2 files changed, 23 insertions(+)

diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
index b5a14a4..d47613c 100644
--- a/drivers/fpga/dfl.c
+++ b/drivers/fpga/dfl.c
@@ -81,6 +81,28 @@ void dfl_fpga_del_port_ops(struct dfl_fpga_port_ops *ops)
}
EXPORT_SYMBOL_GPL(dfl_fpga_del_port_ops);

+/**
+ * dfl_fpga_check_port_id - check the port id
+ * @pdev: port platform device.
+ * @pport_id: port id to compare.
+ *
+ * Return: 1 if port device matches with given port id, otherwise 0.
+ */
+int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id)
+{
+ struct dfl_fpga_port_ops *port_ops = dfl_fpga_get_port_ops(pdev);
+ int port_id;
+
+ if (!port_ops || !port_ops->get_id)
+ return 0;
+
+ port_id = port_ops->get_id(pdev);
+ dfl_fpga_put_port_ops(port_ops);
+
+ return port_id == *(int *)pport_id;
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id);
+
static DEFINE_MUTEX(dfl_id_mutex);

enum dfl_id_type {
diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
index c9d9a01..b0d9b2f 100644
--- a/drivers/fpga/dfl.h
+++ b/drivers/fpga/dfl.h
@@ -151,6 +151,7 @@ struct dfl_fpga_port_ops {
void dfl_fpga_del_port_ops(struct dfl_fpga_port_ops *ops);
struct dfl_fpga_port_ops *dfl_fpga_get_port_ops(struct platform_device *pdev);
void dfl_fpga_put_port_ops(struct dfl_fpga_port_ops *ops);
+int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id);

/**
* struct dfl_feature_driver - sub feature's driver
--
1.8.3.1


2018-05-02 03:09:27

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 01/28] docs: fpga: add a document for FPGA Device Feature List (DFL) Framework Overview

Add a document for FPGA Device Feature List (DFL) Framework Overview.

Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
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.
v4: rename this doc to Device Feature List (DFL) Framework Overview (dfl.txt)
add Device Feature List introduction and re-organize the content.
add description for port reset, bitstream_id/metadata and etc.
v5: remove introduction of the APIs/features which aren't covered in this patchset.
replace "blue/green bitstream" terminology with "static region" and "PR bitstream".
add a "DFL_" prefix to IOCTL APIs introduced by DFL framework.
s/FPGA Bus Device Module/FPGA DFL Device Module/
fix typos, improve descriptions per comments from Alan Tull against v4.
---
Documentation/fpga/dfl.txt | 285 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 285 insertions(+)
create mode 100644 Documentation/fpga/dfl.txt

diff --git a/Documentation/fpga/dfl.txt b/Documentation/fpga/dfl.txt
new file mode 100644
index 0000000..ba2a1ee
--- /dev/null
+++ b/Documentation/fpga/dfl.txt
@@ -0,0 +1,285 @@
+===============================================================================
+ FPGA Device Feature List (DFL) Framework Overview
+-------------------------------------------------------------------------------
+ Enno Luebbers <[email protected]>
+ Xiao Guangrong <[email protected]>
+ Wu Hao <[email protected]>
+
+The Device Feature List (DFL) FPGA framework (and drivers according to this
+this framework) hides the very details of low layer hardwares and provides
+unified interfaces to userspace. Applications could use these interfaces to
+configure, enumerate, open and access FPGA accelerators on platforms which
+implemented the DFL in the device memory. Besides this, the DFL framework
+enables system level management functions such as FPGA reconfiguration.
+
+
+Device Feature List (DFL) Overview
+==================================
+Device Feature List (DFL) defines a linked list of feature headers within the
+device MMIO space to provide an extensible way of adding features. Software can
+walk through these predefined data structures to enumerate FPGA features:
+FPGA Interface Unit (FIU), Accelerated Function Unit (AFU) and Private Features,
+as illustrated below:
+
+ Header Header Header Header
+ +----------+ +-->+----------+ +-->+----------+ +-->+----------+
+ | Type | | | Type | | | Type | | | Type |
+ | FIU | | | Private | | | Private | | | Private |
+ +----------+ | | Feature | | | Feature | | | Feature |
+ | Next_DFH |--+ +----------+ | +----------+ | +----------+
+ +----------+ | Next_DFH |--+ | Next_DFH |--+ | Next_DFH |--> NULL
+ | ID | +----------+ +----------+ +----------+
+ +----------+ | ID | | ID | | ID |
+ | Next_AFU |--+ +----------+ +----------+ +----------+
+ +----------+ | | Feature | | Feature | | Feature |
+ | Header | | | Register | | Register | | Register |
+ | Register | | | Set | | Set | | Set |
+ | Set | | +----------+ +----------+ +----------+
+ +----------+ | Header
+ +-->+----------+
+ | Type |
+ | AFU |
+ +----------+
+ | Next_DFH |--> NULL
+ +----------+
+ | GUID |
+ +----------+
+ | Header |
+ | Register |
+ | Set |
+ +----------+
+
+FPGA Interface Unit (FIU) represents a standalone functional unit for the
+interface to FPGA, e.g the FPGA Management Engine (FME) and Port (more
+descriptions on FME and Port in later sections).
+
+Accelerated Function Unit (AFU) represents a FPGA programmable region, and
+always connects to a FIU (e.g a Port) as its child as illustrated above.
+
+Private Features represent sub features of the FIU and AFU. They could be
+various function blocks with different IDs, but all private features which
+belong to the same FIU or AFU, must be linked to one list via the Next Device
+Feature Header (Next_DFH) pointer.
+
+Each FIU, AFU and Private Feature could implement its own functional registers.
+The functional register set for FIU and AFU, is named as Header Register Set,
+e.g FME Header Register Set, and the one for Private Feature, is named as
+Feature Register Set, e.g FME Partial Reconfiguration Feature Register Set.
+
+This Device Feature List provides a way of linking features together, it's
+convenient for software to locate each feature by walking through this list,
+and can be implemented in register regions of any FPGA device.
+
+
+FIU - FME (FPGA Management Engine)
+==================================
+The FPGA Management Engine performs reconfiguration, and other infrastructure
+functions. Each FPGA device only has one FME.
+
+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 (DFL_FPGA_GET_API_VERSION)
+ Check for extensions (DFL_FPGA_CHECK_EXTENSION)
+ Program bitstream (DFL_FPGA_FME_PORT_PR)
+
+More functions are exposed through sysfs
+(/sys/class/fpga_region/regionX/dfl-fme.n/):
+
+ Read bitstream ID (bitstream_id)
+ bitstream_id indicates version of the static FPGA region.
+
+ Read bitstream metadata (bitstream_metadata)
+ bitstream_metadata includes detailed information of static FPGA region,
+ e.g synthesis date and seed.
+
+ Read number of ports (ports_num)
+ one FPGA device may have more than 1 port, this sysfs interface indicates
+ how many ports the FPGA device has.
+
+
+FIU - PORT
+==========
+A port represents the interface between the static FPGA fabric and a partially
+reconfigurable region containing an AFU. It controls the communication from SW
+to the accelerator and exposes features such as reset and debug. Each FPGA
+device may have more than 1 port, but always 1 AFU per port.
+
+
+AFU
+===
+An AFU is attached to a port FIU and exposes a fixed length 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 (DFL_FPGA_GET_API_VERSION)
+ Check for extensions (DFL_FPGA_CHECK_EXTENSION)
+ Get port info (DFL_FPGA_PORT_GET_INFO)
+ Get MMIO region info (DFL_FPGA_PORT_GET_REGION_INFO)
+ Map DMA buffer (DFL_FPGA_PORT_DMA_MAP)
+ Unmap DMA buffer (DFL_FPGA_PORT_DMA_UNMAP)
+ Reset AFU (*DFL_FPGA_PORT_RESET)
+
+*DFL_FPGA_PORT_RESET: reset the FPGA Port and its AFU. Userspace can do Port
+reset at any time, e.g during DMA or Partial Reconfiguration. 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.
+
+User-space applications can also mmap() accelerator MMIO regions.
+
+More functions are exposed through sysfs:
+(/sys/class/fpga_region/<regionX>/<dfl-port.m>/):
+
+ Read Accelerator GUID (afu_id)
+ afu_id indicates which PR bitstream is programmed to this AFU.
+
+
+DFL Framework Overview
+======================
+
+ +----------+ +--------+ +--------+ +--------+
+ | FME | | AFU | | AFU | | AFU |
+ | Module | | Module | | Module | | Module |
+ +----------+ +--------+ +--------+ +--------+
+ +-----------------------+
+ | FPGA Container Device | Device Feature List
+ | (FPGA Base Region) | Framework
+ +-----------------------+
+--------------------------------------------------------------------
+ +----------------------------+
+ | FPGA DFL Device Module |
+ | (e.g PCIE/Platform Device) |
+ +----------------------------+
+ +------------------------+
+ | FPGA Hardware Device |
+ +------------------------+
+
+DFL framework in kernel provides common interfaces to create container device
+(FPGA base region), discover feature devices and their private features from the
+given Device Feature Lists, and create platform devices for feature devices
+(e.g FME, Port and AFU) with related resources under the container device. It
+also abstracts operations for the private features and exposes common ops to
+feature device drivers.
+
+The FPGA DFL Device could be different hardwares, e.g PCIe device, platform
+device and etc. Its driver module is always loaded first once the device is
+created by the system. This driver plays an infrastructural role in the
+driver architecture. It locates the DFLs in the device memory, handles them
+and related resources to common interfaces from DFL framework for enumeration.
+(Please refer to drivers/fpga/dfl.c for detailed enumeration APIs).
+
+The FPGA Management Engine (FME) driver is a platform driver which is loaded
+automatically after FME platform device creation from the DFL device module. It
+provides the key features for FPGA management, including:
+
+ a) Expose static FPGA region information, e.g version and metadata.
+ Users can read related information 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 a DFL_FPGA_FME_PORT_PR ioctl from user, it invokes the
+ common interface function from FPGA Region to complete the partial
+ reconfiguration of the PR bitstream to the given port.
+
+Similar to 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 functions.
+
+After feature platform devices creation, matched platform drivers will be loaded
+automatically to handle different functionalities. Please refer to next sections
+for detailed information on functional units which have been already implemented
+under this DFL framework.
+
+
+Partial Reconfiguration
+=======================
+As mentioned above, accelerators can be reconfigured through partial
+reconfiguration of a PR bitstream file. The PR bitstream file must have been
+generated for the exact static FPGA region 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 compatibility ID noted in the header of PR bitstream file against
+the compat_id exposed by the target FPGA region. This check is usually done by
+userspace before calling the reconfiguration IOCTL.
+
+
+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 DFL based 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 "dfl-port.n" or "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/dfl-fme.0
+ /sys/class/fpga_region/region0/dfl-port.0
+ /sys/class/fpga_region/region0/dfl-port.1
+ ...
+
+ /sys/class/fpga_region/region3/dfl-fme.1
+ /sys/class/fpga_region/region3/dfl-port.2
+ /sys/class/fpga_region/region3/dfl-port.3
+ ...
+
+In general, the FME/AFU sysfs interfaces are named as follows:
+
+ /sys/class/fpga_region/<regionX>/<dfl-fme.n>/
+ /sys/class/fpga_region/<regionX>/<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>/<dfl-fme.n>/dev
+ /sys/class/fpga_region/<regionX>/<dfl-port.n>/dev
+
+
+Add new FIUs support
+====================
+It's possible that developers made some new function blocks (FIUs) under this
+DFL framework, then new platform device driver needs to be developed for the
+new feature dev (FIU) following the same way as existing feature dev drivers
+(e.g FME and Port/AFU platform device driver). Besides that, it requires
+modification on DFL framework enumeration code too, for new FIU type detection
+and related platform devices creation.
+
+
+Add new private features support
+================================
+In some cases, we may need to add some new private features to existing FIUs
+(e.g FME or Port). Developers don't need to touch enumeration code in DFL
+framework, as each private feature will be parsed automatically, and related
+mmio resources can be found under FIU platform device created by DFL framework.
+Developer only needs to provide a sub feature driver with matched feature id.
+FME Partial Reconfiguration Sub Feature driver (see drivers/fpga/dfl-fme-pr.c)
+could be a reference.
+
+
+Open discussion
+===============
+FME driver exports one ioctl (DFL_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


2018-05-02 03:09:29

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 04/28] fpga: mgr: add compat_id support

This patch introduces compat_id support to fpga manager, it adds
a fpga_compat_id pointer to fpga manager data structure to allow
fpga manager drivers to save the compatibility id. This compat_id
could be used for compatibility checking before doing partial
reconfiguration to associated fpga regions.

Signed-off-by: Wu Hao <[email protected]>
---
include/linux/fpga/fpga-mgr.h | 13 +++++++++++++
1 file changed, 13 insertions(+)

diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 802eac8..f163f22 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -147,11 +147,23 @@ struct fpga_manager_ops {
#define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR BIT(4)

/**
+ * struct fpga_compat_id - id for compatibility check
+ *
+ * @id_h: high 64bit of the compat_id
+ * @id_l: low 64bit of the compat_id
+ */
+struct fpga_compat_id {
+ u64 id_h;
+ u64 id_l;
+};
+
+/**
* struct fpga_manager - fpga manager structure
* @name: name of low level fpga manager
* @dev: fpga manager device
* @ref_mutex: only allows one reference to fpga manager
* @state: state of fpga manager
+ * @compat_id: FPGA manager id for compatibility check.
* @mops: pointer to struct of fpga manager ops
* @priv: low level driver private date
*/
@@ -160,6 +172,7 @@ struct fpga_manager {
struct device dev;
struct mutex ref_mutex;
enum fpga_mgr_states state;
+ struct fpga_compat_id *compat_id;
const struct fpga_manager_ops *mops;
void *priv;
};
--
1.8.3.1


2018-05-02 03:09:49

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 02/28] fpga: mgr: add region_id to fpga_image_info

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 <[email protected]>
Acked-by: Moritz Fischer <[email protected]>
Acked-by: Alan Tull <[email protected]>
---
v3: add one line comment for region_id
v4: add Acked-by from Moritz and Alan.
---
include/linux/fpga/fpga-mgr.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
index 1266c11..eecfca5 100644
--- a/include/linux/fpga/fpga-mgr.h
+++ b/include/linux/fpga/fpga-mgr.h
@@ -88,6 +88,7 @@ enum fpga_mgr_states {
* @sgt: scatter/gather table containing FPGA image
* @buf: contiguous buffer containing FPGA image
* @count: size of buf
+ * @region_id: id of target region
* @dev: device that owns this
* @overlay: Device Tree overlay
*/
@@ -100,6 +101,7 @@ struct fpga_image_info {
struct sg_table *sgt;
const char *buf;
size_t count;
+ int region_id;
struct device *dev;
#ifdef CONFIG_OF
struct device_node *overlay;
--
1.8.3.1


2018-05-02 03:10:11

by Wu Hao

[permalink] [raw]
Subject: [PATCH v5 06/28] fpga: add device feature list support

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 dfl_fpga_enum_info data structure. Please
refer to below description for APIs added for enumeration.

Functions for enumeration information preparation:
*dfl_fpga_enum_info_alloc
allocate enumeration information data structure.

*dfl_fpga_enum_info_add_dfl
add a device feature list to dfl_fpga_enum_info data structure.

*dfl_fpga_enum_info_free
free dfl_fpga_enum_info data structure and related resources.

Functions for feature device enumeration:
*dfl_fpga_enumerate_feature_devs
enumerate feature devices and return container device.

*dfl_fpga_remove_feature_devs
remove feature devices under given container device.

Signed-off-by: Tim Whisonant <[email protected]>
Signed-off-by: Enno Luebbers <[email protected]>
Signed-off-by: Shiva Rao <[email protected]>
Signed-off-by: Christopher Rauer <[email protected]>
Signed-off-by: Zhang Yi <[email protected]>
Signed-off-by: Xiao Guangrong <[email protected]>
Signed-off-by: Wu Hao <[email protected]>
---
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.
v4: fix SPDX license issue
rename files to dfl.[ch], fix typo and add more comments.
remove static feature_info tables for FME and Port.
remove check on next_afu link list as only FIU has next_afu ptr.
remove unused macro in header file.
add more comments for functions.
v5: add "dfl_" prefix to functions and data structures.
remove port related functions from DFL framework.
use BIT_ULL for 64bit register definition.
save dfl_fpga_cdev in pdata for feature platform devices.
rebase due to fpga region api changes.
---
drivers/fpga/Kconfig | 16 ++
drivers/fpga/Makefile | 3 +
drivers/fpga/dfl.c | 720 ++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/fpga/dfl.h | 279 +++++++++++++++++++
4 files changed, 1018 insertions(+)
create mode 100644 drivers/fpga/dfl.c
create mode 100644 drivers/fpga/dfl.h

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index f47ef84..01ad31f 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -124,4 +124,20 @@ config OF_FPGA_REGION
Support for loading FPGA images by applying a Device Tree
overlay.

+config FPGA_DFL
+ tristate "FPGA Device Feature List (DFL) support"
+ select FPGA_BRIDGE
+ select FPGA_REGION
+ help
+ 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 for FPGA.
+ Driver can walk through the feature headers to enumerate feature
+ devices (e.g FPGA Management Engine, Port and Accelerator
+ Function Unit) and their private features for target FPGA devices.
+
+ Select this option to enable common support for Field-Programmable
+ Gate Array (FPGA) solutions which implement Device Feature List.
+ It provides enumeration APIs, and feature device infrastructure.
+
endif # FPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 3cb276a..c4c62b9 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -27,3 +27,6 @@ obj-$(CONFIG_XILINX_PR_DECOUPLER) += xilinx-pr-decoupler.o
# High Level Interfaces
obj-$(CONFIG_FPGA_REGION) += fpga-region.o
obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
+
+# FPGA Device Feature List Support
+obj-$(CONFIG_FPGA_DFL) += dfl.o
diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
new file mode 100644
index 0000000..c1462e9
--- /dev/null
+++ b/drivers/fpga/dfl.c
@@ -0,0 +1,720 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for FPGA Device Feature List (DFL) Support
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Zhang Yi <[email protected]>
+ * Wu Hao <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ */
+#include <linux/module.h>
+
+#include "dfl.h"
+
+static DEFINE_MUTEX(dfl_id_mutex);
+
+enum dfl_id_type {
+ FME_ID, /* fme id allocation and mapping */
+ PORT_ID, /* port id allocation and mapping */
+ DFL_ID_MAX,
+};
+
+/* it is protected by dfl_id_mutex */
+static struct idr dfl_ids[DFL_ID_MAX];
+
+static void dfl_ids_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dfl_ids); i++)
+ idr_init(dfl_ids + i);
+}
+
+static void dfl_ids_destroy(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dfl_ids); i++)
+ idr_destroy(dfl_ids + i);
+}
+
+static int alloc_dfl_id(enum dfl_id_type type, struct device *dev)
+{
+ int id;
+
+ WARN_ON(type >= DFL_ID_MAX);
+ mutex_lock(&dfl_id_mutex);
+ id = idr_alloc(dfl_ids + type, dev, 0, 0, GFP_KERNEL);
+ mutex_unlock(&dfl_id_mutex);
+
+ return id;
+}
+
+static void free_dfl_id(enum dfl_id_type type, int id)
+{
+ WARN_ON(type >= DFL_ID_MAX);
+ mutex_lock(&dfl_id_mutex);
+ idr_remove(dfl_ids + type, id);
+ mutex_unlock(&dfl_id_mutex);
+}
+
+static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
+{
+ if (!strcmp(pdev->name, DFL_FPGA_FEATURE_DEV_FME))
+ return FME_ID;
+
+ if (!strcmp(pdev->name, DFL_FPGA_FEATURE_DEV_PORT))
+ return PORT_ID;
+
+ WARN_ON(1);
+
+ return DFL_ID_MAX;
+}
+
+/**
+ * struct 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.
+ * @ioaddr: header register region address of feature device in enumeration.
+ * @sub_features: a sub features link list for feature device in enumeration.
+ * @feature_num: number of sub features for feature device in enumeration.
+ */
+struct build_feature_devs_info {
+ struct device *dev;
+ struct dfl_fpga_cdev *cdev;
+ struct platform_device *feature_dev;
+ void __iomem *ioaddr;
+ struct list_head sub_features;
+ int feature_num;
+};
+
+/**
+ * struct dfl_feature_info - sub feature info collected during feature dev build
+ *
+ * @fid: id of this sub feature.
+ * @mmio_res: mmio resource of this sub feature.
+ * @ioaddr: mapped base address of mmio resource.
+ * @node: node in sub_features link list.
+ */
+struct dfl_feature_info {
+ u64 fid;
+ struct resource mmio_res;
+ void __iomem *ioaddr;
+ struct list_head node;
+};
+
+static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev,
+ struct platform_device *port)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(&port->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)
+{
+ struct platform_device *fdev = binfo->feature_dev;
+ struct dfl_feature_platform_data *pdata;
+ struct dfl_feature_info *finfo, *p;
+ int ret, index = 0;
+
+ if (!fdev)
+ return 0;
+
+ /*
+ * 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(dfl_feature_platform_data_size(binfo->feature_num),
+ GFP_KERNEL);
+ if (pdata) {
+ pdata->dev = fdev;
+ pdata->num = binfo->feature_num;
+ pdata->dfl_cdev = binfo->cdev;
+ mutex_init(&pdata->lock);
+ } else {
+ return -ENOMEM;
+ }
+
+ /*
+ * 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;
+
+ /* each sub feature has one MMIO resource */
+ fdev->num_resources = binfo->feature_num;
+ fdev->resource = kcalloc(binfo->feature_num, sizeof(*fdev->resource),
+ GFP_KERNEL);
+ if (!fdev->resource)
+ return -ENOMEM;
+
+ /* fill features and resource information for feature dev */
+ list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
+ struct dfl_feature *feature = &pdata->features[index];
+
+ /* save resource information for each feature */
+ feature->id = finfo->fid;
+ feature->resource_index = index;
+ feature->ioaddr = finfo->ioaddr;
+ fdev->resource[index++] = finfo->mmio_res;
+
+ list_del(&finfo->node);
+ kfree(finfo);
+ }
+
+ ret = platform_device_add(binfo->feature_dev);
+ if (!ret) {
+ if (feature_dev_id_type(binfo->feature_dev) == PORT_ID)
+ dfl_fpga_cdev_add_port_dev(binfo->cdev,
+ binfo->feature_dev);
+ else
+ 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 dfl_id_type type, const char *name,
+ void __iomem *ioaddr)
+{
+ struct platform_device *fdev;
+ 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;
+ binfo->feature_num = 0;
+ binfo->ioaddr = ioaddr;
+ INIT_LIST_HEAD(&binfo->sub_features);
+
+ fdev->id = alloc_dfl_id(type, &fdev->dev);
+ if (fdev->id < 0)
+ return fdev->id;
+
+ fdev->dev.parent = &binfo->cdev->region->dev;
+
+ return 0;
+}
+
+static void build_info_free(struct build_feature_devs_info *binfo)
+{
+ struct dfl_feature_info *finfo, *p;
+
+ /*
+ * it is a valid id, free it. See comments in
+ * build_info_create_dev()
+ */
+ if (binfo->feature_dev && binfo->feature_dev->id >= 0) {
+ free_dfl_id(feature_dev_id_type(binfo->feature_dev),
+ binfo->feature_dev->id);
+
+ list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
+ list_del(&finfo->node);
+ kfree(finfo);
+ }
+ }
+
+ platform_device_put(binfo->feature_dev);
+
+ devm_kfree(binfo->dev, binfo);
+}
+
+static inline u32 feature_size(void __iomem *start)
+{
+ u64 v = readq(start + DFH);
+ u32 ofst = FIELD_GET(DFH_NEXT_HDR_OFST, v);
+ /* workaround for private features with invalid size, use 4K instead */
+ return ofst ? ofst : 4096;
+}
+
+static u64 feature_id(void __iomem *start)
+{
+ u64 v = readq(start + DFH);
+ u16 id = FIELD_GET(DFH_ID, v);
+ u8 type = FIELD_GET(DFH_TYPE, v);
+
+ if (type == DFH_TYPE_FIU)
+ return FEATURE_ID_FIU_HEADER;
+ else if (type == DFH_TYPE_PRIVATE)
+ return id;
+ else if (type == DFH_TYPE_AFU)
+ return FEATURE_ID_AFU;
+
+ WARN_ON(1);
+ return 0;
+}
+
+/*
+ * when create sub feature instances, for private features, it doesn't need
+ * to provide resource size and feature id as they could be read from DFH
+ * register. For afu sub feature, its register region only contains user
+ * defined registers, so never trust any information from it, just use the
+ * resource size information provided by its parent FIU.
+ */
+static int
+create_feature_instance(struct build_feature_devs_info *binfo,
+ struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst,
+ resource_size_t size, u64 fid)
+{
+ struct dfl_feature_info *finfo;
+
+ /* read feature size and id if inputs are invalid */
+ size = size ? size : feature_size(dfl->ioaddr + ofst);
+ fid = fid ? fid : feature_id(dfl->ioaddr + ofst);
+
+ if (dfl->len - ofst < size)
+ return -EINVAL;
+
+ finfo = kzalloc(sizeof(*finfo), GFP_KERNEL);
+ if (!finfo)
+ return -ENOMEM;
+
+ finfo->fid = fid;
+ finfo->mmio_res.start = dfl->start + ofst;
+ finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
+ finfo->mmio_res.flags = IORESOURCE_MEM;
+ finfo->ioaddr = dfl->ioaddr + ofst;
+
+ list_add_tail(&finfo->node, &binfo->sub_features);
+ binfo->feature_num++;
+
+ return 0;
+}
+
+static int parse_feature_fme(struct build_feature_devs_info *binfo,
+ struct dfl_fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ int ret;
+
+ ret = build_info_create_dev(binfo, FME_ID, DFL_FPGA_FEATURE_DEV_FME,
+ dfl->ioaddr + ofst);
+ if (ret)
+ return ret;
+
+ return create_feature_instance(binfo, dfl, ofst, 0, 0);
+}
+
+static int parse_feature_port(struct build_feature_devs_info *binfo,
+ struct dfl_fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ int ret;
+
+ ret = build_info_create_dev(binfo, PORT_ID, DFL_FPGA_FEATURE_DEV_PORT,
+ dfl->ioaddr + ofst);
+ if (ret)
+ return ret;
+
+ return create_feature_instance(binfo, dfl, ofst, 0, 0);
+}
+
+static int parse_feature_port_afu(struct build_feature_devs_info *binfo,
+ struct dfl_fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ u64 v = readq(binfo->ioaddr + PORT_HDR_CAP);
+ u32 size = FIELD_GET(PORT_CAP_MMIO_SIZE, v) << 10;
+
+ WARN_ON(!size);
+
+ return create_feature_instance(binfo, dfl, ofst, size, FEATURE_ID_AFU);
+}
+
+static int parse_feature_afu(struct build_feature_devs_info *binfo,
+ struct dfl_fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ if (!binfo->feature_dev) {
+ dev_err(binfo->dev, "this AFU does not belong to any FIU.\n");
+ return -EINVAL;
+ }
+
+ switch (feature_dev_id_type(binfo->feature_dev)) {
+ case PORT_ID:
+ return parse_feature_port_afu(binfo, dfl, ofst);
+ default:
+ dev_info(binfo->dev, "AFU belonging to FIU %s is not supported yet.\n",
+ binfo->feature_dev->name);
+ }
+
+ return 0;
+}
+
+static int parse_feature_fiu(struct build_feature_devs_info *binfo,
+ struct dfl_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:
+ ret = parse_feature_fme(binfo, dfl, ofst);
+ break;
+ case DFH_ID_FIU_PORT:
+ ret = parse_feature_port(binfo, dfl, ofst);
+ break;
+ default:
+ dev_info(binfo->dev, "FIU TYPE %d is not supported yet.\n",
+ id);
+ }
+
+ if (ret)
+ return ret;
+
+ /* Find and parse FIU's child AFU via its NEXT_AFU register */
+ v = readq(dfl->ioaddr + ofst + NEXT_AFU);
+
+ offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v);
+ if (offset)
+ return parse_feature_afu(binfo, dfl, ofst + offset);
+
+ dev_dbg(binfo->dev, "No AFUs detected on FIU %d\n", id);
+
+ return ret;
+}
+
+static int parse_feature_private(struct build_feature_devs_info *binfo,
+ struct dfl_fpga_enum_dfl *dfl,
+ resource_size_t ofst)
+{
+ if (!binfo->feature_dev) {
+ dev_err(binfo->dev, "the private feature %llx does not belong to any AFU.\n",
+ (unsigned long long)feature_id(dfl->ioaddr + ofst));
+ return -EINVAL;
+ }
+
+ return create_feature_instance(binfo, dfl, ofst, 0, 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 dfl_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_afu(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 dfl_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 dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev)
+{
+ struct dfl_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(dfl_fpga_enum_info_alloc);
+
+void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info)
+{
+ struct dfl_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(dfl_fpga_enum_info_free);
+
+/**
+ * dfl_fpga_enum_info_add_dfl - add info of a device feature list to enum info
+ *
+ * @info: ptr to dfl_fpga_enum_info
+ * @start: mmio resource address of the device feature list.
+ * @len: mmio resource length of the device feature list.
+ * @ioaddr: mapped mmio resource address of the device feature list.
+ *
+ * One FPGA device may have 1 or more Device Feature Lists (DFLs), use this
+ * function to add information of each DFL to common data structure for next
+ * step enumeration.
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
+ resource_size_t start, resource_size_t len,
+ void __iomem *ioaddr)
+{
+ struct dfl_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(dfl_fpga_enum_info_add_dfl);
+
+static int remove_feature_dev(struct device *dev, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ enum dfl_id_type type = feature_dev_id_type(pdev);
+ int id = pdev->id;
+
+ platform_device_unregister(pdev);
+
+ free_dfl_id(type, id);
+
+ return 0;
+}
+
+static void remove_feature_devs(struct dfl_fpga_cdev *cdev)
+{
+ device_for_each_child(&cdev->region->dev, NULL, remove_feature_dev);
+}
+
+/**
+ * dfl_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: dfl_fpga_cdev struct on success, -errno on failure
+ */
+struct dfl_fpga_cdev *
+dfl_fpga_enumerate_feature_devs(struct dfl_fpga_enum_info *info)
+{
+ struct build_feature_devs_info *binfo;
+ struct dfl_fpga_enum_dfl *dfl;
+ struct dfl_fpga_cdev *cdev;
+ 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->region = fpga_region_create(info->dev, NULL, NULL);
+ if (!cdev->region) {
+ ret = -ENOMEM;
+ goto free_region_exit;
+ }
+
+ cdev->parent = info->dev;
+ mutex_init(&cdev->lock);
+ INIT_LIST_HEAD(&cdev->port_dev_list);
+
+ 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_region_exit:
+ fpga_region_free(cdev->region);
+free_cdev_exit:
+ devm_kfree(info->dev, cdev);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(dfl_fpga_enumerate_feature_devs);
+
+/**
+ * dfl_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 dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev)
+{
+ struct dfl_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(dfl_fpga_remove_feature_devs);
+
+static int __init dfl_fpga_init(void)
+{
+ dfl_ids_init();
+
+ return 0;
+}
+
+static void __exit dfl_fpga_exit(void)
+{
+ dfl_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");
diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
new file mode 100644
index 0000000..2ede915
--- /dev/null
+++ b/drivers/fpga/dfl.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver Header File for FPGA Device Feature List (DFL) Support
+ *
+ * Copyright (C) 2017 Intel Corporation, Inc.
+ *
+ * Authors:
+ * Kang Luwei <[email protected]>
+ * Zhang Yi <[email protected]>
+ * Wu Hao <[email protected]>
+ * Xiao Guangrong <[email protected]>
+ */
+
+#ifndef __FPGA_DFL_H
+#define __FPGA_DFL_H
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/iopoll.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+#include <linux/fpga/fpga-region.h>
+
+/* maximum supported number of ports */
+#define MAX_DFL_FPGA_PORT_NUM 4
+/* plus one for fme device */
+#define MAX_DFL_FEATURE_DEV_NUM (MAX_DFL_FPGA_PORT_NUM + 1)
+
+/* Reserved 0x0 for Header Group Register and 0xff for AFU */
+#define FEATURE_ID_FIU_HEADER 0x0
+#define FEATURE_ID_AFU 0xff
+
+#define FME_FEATURE_ID_HEADER FEATURE_ID_FIU_HEADER
+#define FME_FEATURE_ID_THERMAL_MGMT 0x1
+#define FME_FEATURE_ID_POWER_MGMT 0x2
+#define FME_FEATURE_ID_GLOBAL_IPERF 0x3
+#define FME_FEATURE_ID_GLOBAL_ERR 0x4
+#define FME_FEATURE_ID_PR_MGMT 0x5
+#define FME_FEATURE_ID_HSSI 0x6
+#define FME_FEATURE_ID_GLOBAL_DPERF 0x7
+
+#define PORT_FEATURE_ID_HEADER FEATURE_ID_FIU_HEADER
+#define PORT_FEATURE_ID_AFU FEATURE_ID_AFU
+#define PORT_FEATURE_ID_ERROR 0x10
+#define PORT_FEATURE_ID_UMSG 0x11
+#define PORT_FEATURE_ID_UINT 0x12
+#define PORT_FEATURE_ID_STP 0x13
+
+/*
+ * Device Feature Header Register Set
+ *
+ * For FIUs, they all have DFH + GUID + NEXT_AFU as common header registers.
+ * For AFUs, they have DFH + GUID as common header registers.
+ * For private features, they only have DFH register as common header.
+ */
+#define DFH 0x0
+#define GUID_L 0x8
+#define GUID_H 0x10
+#define NEXT_AFU 0x18
+
+#define DFH_SIZE 0x8
+
+/* Device Feature Header Register Bitfield */
+#define DFH_ID GENMASK_ULL(11, 0) /* Feature ID */
+#define DFH_ID_FIU_FME 0
+#define DFH_ID_FIU_PORT 1
+#define DFH_REVISION GENMASK_ULL(15, 12) /* Feature revision */
+#define DFH_NEXT_HDR_OFST GENMASK_ULL(39, 16) /* Offset to next DFH */
+#define DFH_EOL BIT_ULL(40) /* End of list */
+#define DFH_TYPE GENMASK_ULL(63, 60) /* Feature type */
+#define DFH_TYPE_AFU 1
+#define DFH_TYPE_PRIVATE 3
+#define DFH_TYPE_FIU 4
+
+/* Next AFU Register Bitfield */
+#define NEXT_AFU_NEXT_DFH_OFST GENMASK_ULL(23, 0) /* Offset to next AFU */
+
+/* FME Header Register Set */
+#define FME_HDR_DFH DFH
+#define FME_HDR_GUID_L GUID_L
+#define FME_HDR_GUID_H GUID_H
+#define FME_HDR_NEXT_AFU NEXT_AFU
+#define FME_HDR_CAP 0x30
+#define FME_HDR_PORT_OFST(n) (0x38 + ((n) * 0x8))
+#define FME_HDR_BITSTREAM_ID 0x60
+#define FME_HDR_BITSTREAM_MD 0x68
+
+/* FME Fab Capability Register Bitfield */
+#define FME_CAP_FABRIC_VERID GENMASK_ULL(7, 0) /* Fabric version ID */
+#define FME_CAP_SOCKET_ID BIT_ULL(8) /* Socket ID */
+#define FME_CAP_PCIE0_LINK_AVL BIT_ULL(12) /* PCIE0 Link */
+#define FME_CAP_PCIE1_LINK_AVL BIT_ULL(13) /* PCIE1 Link */
+#define FME_CAP_COHR_LINK_AVL BIT_ULL(14) /* Coherent Link */
+#define FME_CAP_IOMMU_AVL BIT_ULL(16) /* IOMMU available */
+#define FME_CAP_NUM_PORTS GENMASK_ULL(19, 17) /* Number of ports */
+#define FME_CAP_ADDR_WIDTH GENMASK_ULL(29, 24) /* Address bus width */
+#define FME_CAP_CACHE_SIZE GENMASK_ULL(43, 32) /* cache size in KB */
+#define FME_CAP_CACHE_ASSOC GENMASK_ULL(47, 44) /* Associativity */
+
+/* FME Port Offset Register Bitfield */
+/* Offset to port device feature header */
+#define FME_PORT_OFST_DFH_OFST GENMASK_ULL(23, 0)
+/* PCI Bar ID for this port */
+#define FME_PORT_OFST_BAR_ID GENMASK_ULL(34, 32)
+/* AFU MMIO access permission. 1 - VF, 0 - PF. */
+#define FME_PORT_OFST_ACC_CTRL BIT_ULL(55)
+#define FME_PORT_OFST_ACC_PF 0
+#define FME_PORT_OFST_ACC_VF 1
+#define FME_PORT_OFST_IMP BIT_ULL(60)
+
+/* PORT Header Register Set */
+#define PORT_HDR_DFH DFH
+#define PORT_HDR_GUID_L GUID_L
+#define PORT_HDR_GUID_H GUID_H
+#define PORT_HDR_NEXT_AFU NEXT_AFU
+#define PORT_HDR_CAP 0x30
+#define PORT_HDR_CTRL 0x38
+
+/* Port Capability Register Bitfield */
+#define PORT_CAP_PORT_NUM GENMASK_ULL(1, 0) /* ID of this port */
+#define PORT_CAP_MMIO_SIZE GENMASK_ULL(23, 8) /* MMIO size in KB */
+#define PORT_CAP_SUPP_INT_NUM GENMASK_ULL(35, 32) /* Interrupts num */
+
+/* Port Control Register Bitfield */
+#define PORT_CTRL_SFTRST BIT_ULL(0) /* Port soft reset */
+/* Latency tolerance reporting. '1' >= 40us, '0' < 40us.*/
+#define PORT_CTRL_LATENCY BIT_ULL(2)
+#define PORT_CTRL_SFTRST_ACK BIT_ULL(4) /* HW ack for reset */
+
+/**
+ * struct dfl_feature - sub feature of the feature devices
+ *
+ * @id: sub feature id.
+ * @resource_index: each sub feature has one mmio resource for its registers.
+ * this index is used to find its mmio resource from the
+ * feature dev (platform device)'s reources.
+ * @ioaddr: mapped mmio resource address.
+ */
+struct dfl_feature {
+ u64 id;
+ int resource_index;
+ void __iomem *ioaddr;
+};
+
+/**
+ * struct dfl_feature_platform_data - platform data for feature devices
+ *
+ * @node: node to link feature devs to container device's port_dev_list.
+ * @lock: mutex to protect platform data.
+ * @dev: ptr to platform device linked with this platform data.
+ * @dfl_cdev: ptr to container device.
+ * @disable_count: count for port disable.
+ * @num: number for sub features.
+ * @features: sub features of this feature dev.
+ */
+struct dfl_feature_platform_data {
+ struct list_head node;
+ struct mutex lock;
+ struct platform_device *dev;
+ struct dfl_fpga_cdev *dfl_cdev;
+ unsigned int disable_count;
+
+ int num;
+ struct dfl_feature features[0];
+};
+
+#define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
+#define DFL_FPGA_FEATURE_DEV_PORT "dfl-port"
+
+static inline int dfl_feature_platform_data_size(const int num)
+{
+ return sizeof(struct dfl_feature_platform_data) +
+ num * sizeof(struct dfl_feature);
+}
+
+#define dfl_fpga_dev_for_each_feature(pdata, feature) \
+ for ((feature) = (pdata)->features; \
+ (feature) < (pdata)->features + (pdata)->num; (feature)++)
+
+static inline
+struct dfl_feature *dfl_get_feature_by_id(struct device *dev, u64 id)
+{
+ struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+ struct dfl_feature *feature;
+
+ dfl_fpga_dev_for_each_feature(pdata, feature)
+ if (feature->id == id)
+ return feature;
+
+ return NULL;
+}
+
+static inline
+void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u64 id)
+{
+ struct dfl_feature *feature = dfl_get_feature_by_id(dev, id);
+
+ if (feature && feature->ioaddr)
+ return feature->ioaddr;
+
+ WARN_ON(1);
+ return NULL;
+}
+
+static inline bool dfl_feature_is_fme(void __iomem *base)
+{
+ u64 v = readq(base + DFH);
+
+ return (FIELD_GET(DFH_TYPE, v) == DFH_TYPE_FIU) &&
+ (FIELD_GET(DFH_ID, v) == DFH_ID_FIU_FME);
+}
+
+static inline bool dfl_feature_is_port(void __iomem *base)
+{
+ u64 v = readq(base + DFH);
+
+ return (FIELD_GET(DFH_TYPE, v) == DFH_TYPE_FIU) &&
+ (FIELD_GET(DFH_ID, v) == DFH_ID_FIU_PORT);
+}
+
+/**
+ * struct dfl_fpga_enum_info - DFL FPGA enumeration information
+ *
+ * @dev: parent device.
+ * @dfls: list of device feature lists.
+ */
+struct dfl_fpga_enum_info {
+ struct device *dev;
+ struct list_head dfls;
+};
+
+/**
+ * struct dfl_fpga_enum_dfl - DFL FPGA enumeration device feature list info
+ *
+ * @start: base address of this device feature list.
+ * @len: size of this device feature list.
+ * @ioaddr: mapped base address of this device feature list.
+ * @node: node in list of device feature lists.
+ */
+struct dfl_fpga_enum_dfl {
+ resource_size_t start;
+ resource_size_t len;
+
+ void __iomem *ioaddr;
+
+ struct list_head node;
+};
+
+struct dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev);
+int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
+ resource_size_t start, resource_size_t len,
+ void __iomem *ioaddr);
+void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info);
+
+/**
+ * struct dfl_fpga_cdev - container device of DFL based FPGA
+ *
+ * @parent: parent device of this container device.
+ * @region: base fpga region.
+ * @fme_dev: FME feature device under this container device.
+ * @lock: mutex lock to protect the port device list.
+ * @port_dev_list: list of all port feature devices under this container device.
+ */
+struct dfl_fpga_cdev {
+ struct device *parent;
+ struct fpga_region *region;
+ struct device *fme_dev;
+ struct mutex lock;
+ struct list_head port_dev_list;
+};
+
+struct dfl_fpga_cdev *
+dfl_fpga_enumerate_feature_devs(struct dfl_fpga_enum_info *info);
+void dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev);
+
+#endif /* __FPGA_DFL_H */
--
1.8.3.1


2018-05-03 21:16:23

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 00/28] FPGA Device Feature List (DFL) Device Drivers

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
> Hi All,
>
> Here is v5 patch-series adding drivers for FPGA DFL devices.
>
> This patch series provides a common framework to support FPGA Device Feature
> List (DFL), and also feature dev drivers under this DFL framework to provide
> interfaces for userspace applications to configure, enumerate, open, and
> access FPGA accelerators on DFL based FPGA device and enables system level
> management functions such as FPGA partial reconfiguration, power management
> and virtualization.

Hi Hao,

I've started looking at your v5 here. It's 5212 lines of code, so
it's not tiny.

I see a lot of renaming from 'fpga' to 'dfl', thanks for doing that.

The main thing that stands out at first look is a new method of
registering port ops. It would be better if the fpga bridge framework
were expanded or changed somehow to do that. It's like we have two
bridge frameworks now.

I see that there isn't a way of specifying which FPGA mgr driver to
use, so dfl-fme-mgr.c is tied to dfl-fme-pr.c. It would be better if
the DFL could specify which fpga manager driver to use.

Maybe I'm repeating myself here, the biggest question I'm asking
myself is: If someone had a different FPGA or a different static
region, how much of this code can be reused? I would hope that all of
it could except for the fpga manager/bridge drivers. That was the
intent of the original FPGA framework. I'll have more time next week
to look in more depth.

Alan

>
> 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.
>
> Note this patch series is only verified on DFL based Intel(R) FPGA PCIe
> devices (e.g Intel Server Platform with In-package FPGA and Intel FPGA PCIe
> Acceleration Cards).
>
> Patch 1: add a document for FPGA DFL framework driver overview, including
> Device Feature List (DFL) introduction, the HW architecture, driver
> organization, device enumeration 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 17)
>
> 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 18)
>
> Patch 4-5: add compat_id support in fpga manager and region, this compat
> id is used for compatibility check before further actions (e.g partial
> reconfiguration). (Used by Patch 19 and 22)
>
> Patch 6-11: 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. A global list is added to DFL framework to
> manage port ops from different ports.
>
> Patch 12-13: implement 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 14-17: 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 18-22: 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 23-28: 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 v4:
> - Update the dfl.txt documentation, remove descriptions for the APIs and
> features which are not implemented in this patch series.
> - Add DFL_ / dfl_ prefix for APIs and data structure, to avoid directly
> using DFL_ / fpga_xxx naming.
> - Use "static region" and "PR bistream" instead of "blue bitstream" and
> "green bistream" terminology in description to avoid misunderstanding.
> - Fix building issues caused by BIT() on 64bit register definition and
> missing correct header file for readq and writeq.
> - Remove port specific code in DFL framework, and introduce port ops support
> to resolve the dependency issue between FME driver module and Port driver
> module. (more details in Patch 10).
> - Add compat id to fpga manager, as in case some hardware implements the
> compat id in fpga manager's register, not register belongs to fpga region,
> and its value is shared by all related fpga regions.
> - Pass mapped ioaddr to fme manager platform device from dfl-fme-pr via pdata.
> - Move PR hardware register definition to dfl-fme-mgr.c.
> - Fix other comments from Alan and Moritz, including description improvement,
> coding style issue and etc.
>
> Changes from v3:
> - Fix SPDX license issue.
> - Rename documentation to dfl.txt, add introduction for Device Feature List
> (DFL) and re-organize the content.
> - Rename to FPGA Device Feature List (DFL) drivers from Intel FPGA device
> drivers for better reuse purposes. Unified driver and files to dfl-*.*
> - Remove static feature_info table from common enumeration code, and switch
> to use feature id for sub feature driver matching.
> - Remove next_afu register checking for AFU from common enumeration code.
> - Remove interface_id sysfs for dfl-fme-mgr and use per fpga-region
> compat_id instead. (new patch 13, 15, 19).
> - Add more comments for driver data structures and functions.
> - Fix typos, issues in debug message/commit message and other places.
>
> 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 v4 0/4] fpga: change api, don't use drive data[1]
>
> [1] https://marc.info/?l=linux-fpga&m=152356504900466&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 (22):
> docs: fpga: add a document for FPGA Device Feature List (DFL)
> Framework Overview
> fpga: mgr: add region_id to fpga_image_info
> fpga: mgr: add status for fpga-manager
> fpga: mgr: add compat_id support
> fpga: region: add compat_id support
> fpga: add device feature list support
> fpga: dfl: add chardev support for feature devices
> fpga: dfl: add dfl_fpga_cdev_find_port
> fpga: dfl: add dfl_fpga_port_ops support.
> fpga: dfl: add dfl_fpga_check_port_id function.
> fpga: dfl-pci: add enumeration for feature devices
> fpga: dfl: fme: add DFL_FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls
> support
> fpga: dfl: add fpga manager platform driver for FME
> fpga: dfl: fme-mgr: add compat_id support
> fpga: dfl: add fpga bridge platform driver for FME
> fpga: dfl: add fpga region platform driver for FME
> fpga: dfl: fme-region: add support for compat_id
> fpga: dfl: add FPGA Accelerated Function Unit driver basic framework
> fpga: dfl: afu: add port ops support
> fpga: dfl: afu: add header sub feature support
> fpga: dfl: afu: add DFL_FPGA_GET_API_VERSION/CHECK_EXTENSION ioctls
> support
> fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support
>
> Xiao Guangrong (2):
> fpga: dfl: add feature device infrastructure
> fpga: dfl: afu: add afu sub feature support
>
> Zhang Yi (1):
> fpga: add FPGA DFL PCIe device driver
>
> Documentation/ABI/testing/sysfs-class-fpga-manager | 24 +
> Documentation/ABI/testing/sysfs-class-fpga-region | 9 +
> Documentation/ABI/testing/sysfs-platform-dfl-fme | 23 +
> Documentation/ABI/testing/sysfs-platform-dfl-port | 16 +
> Documentation/fpga/dfl.txt | 285 ++++++
> Documentation/ioctl/ioctl-number.txt | 1 +
> drivers/fpga/Kconfig | 68 ++
> drivers/fpga/Makefile | 14 +
> drivers/fpga/dfl-afu-dma-region.c | 463 +++++++++
> drivers/fpga/dfl-afu-main.c | 636 +++++++++++++
> drivers/fpga/dfl-afu-region.c | 166 ++++
> drivers/fpga/dfl-afu.h | 100 ++
> drivers/fpga/dfl-fme-br.c | 114 +++
> drivers/fpga/dfl-fme-main.c | 279 ++++++
> drivers/fpga/dfl-fme-mgr.c | 349 +++++++
> drivers/fpga/dfl-fme-pr.c | 474 ++++++++++
> drivers/fpga/dfl-fme-pr.h | 84 ++
> drivers/fpga/dfl-fme-region.c | 89 ++
> drivers/fpga/dfl-fme.h | 38 +
> drivers/fpga/dfl-pci.c | 310 ++++++
> drivers/fpga/dfl.c | 1000 ++++++++++++++++++++
> drivers/fpga/dfl.h | 416 ++++++++
> drivers/fpga/fpga-mgr.c | 28 +
> drivers/fpga/fpga-region.c | 22 +
> include/linux/fpga/fpga-mgr.h | 24 +
> include/linux/fpga/fpga-region.h | 2 +
> include/uapi/linux/fpga-dfl.h | 178 ++++
> 27 files changed, 5212 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-region
> create mode 100644 Documentation/ABI/testing/sysfs-platform-dfl-fme
> create mode 100644 Documentation/ABI/testing/sysfs-platform-dfl-port
> create mode 100644 Documentation/fpga/dfl.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-br.c
> create mode 100644 drivers/fpga/dfl-fme-main.c
> create mode 100644 drivers/fpga/dfl-fme-mgr.c
> create mode 100644 drivers/fpga/dfl-fme-pr.c
> create mode 100644 drivers/fpga/dfl-fme-pr.h
> create mode 100644 drivers/fpga/dfl-fme-region.c
> create mode 100644 drivers/fpga/dfl-fme.h
> create mode 100644 drivers/fpga/dfl-pci.c
> create mode 100644 drivers/fpga/dfl.c
> create mode 100644 drivers/fpga/dfl.h
> create mode 100644 include/uapi/linux/fpga-dfl.h
>
> --
> 1.8.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2018-05-04 00:26:36

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 00/28] FPGA Device Feature List (DFL) Device Drivers

On Thu, May 03, 2018 at 04:14:48PM -0500, Alan Tull wrote:
> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
> > Hi All,
> >
> > Here is v5 patch-series adding drivers for FPGA DFL devices.
> >
> > This patch series provides a common framework to support FPGA Device Feature
> > List (DFL), and also feature dev drivers under this DFL framework to provide
> > interfaces for userspace applications to configure, enumerate, open, and
> > access FPGA accelerators on DFL based FPGA device and enables system level
> > management functions such as FPGA partial reconfiguration, power management
> > and virtualization.
>
> Hi Hao,
>

Hi Alan,

Thanks for your review on this patch set.

> I've started looking at your v5 here. It's 5212 lines of code, so
> it's not tiny.
>
> I see a lot of renaming from 'fpga' to 'dfl', thanks for doing that.
>
> The main thing that stands out at first look is a new method of
> registering port ops. It would be better if the fpga bridge framework
> were expanded or changed somehow to do that. It's like we have two
> bridge frameworks now.

I only limit this change in DFL framework, because introduce a port ops
here to DFL framework is to resolve the hard dependency between DFL
driver modules (e.g DFL FME and port), this sounds like a DFL specific
thing, and I am not sure if there is some real need or benefit for other
non-DFL module, so i didn't modify the common fpga bridge code.

>
> I see that there isn't a way of specifying which FPGA mgr driver to
> use, so dfl-fme-mgr.c is tied to dfl-fme-pr.c. It would be better if
> the DFL could specify which fpga manager driver to use.

Hm.. I think we discussed this against v4. It could be a different sub
feature with different id to support different PR hardware. In that
case, we could still reuse sub feature driver provided by dfl-fme-pr.c
(specify a new id which reuses the same pr_mgmt_ops)

{
.id = FME_FEATURE_ID_PR_MGMT,
.ops = &pr_mgmt_ops,
},

Inside dfl-fme-pr.c, it could parse the different id to create different
platform devices for different FPGA mgrs.

Could we leave this a later change? as i don't have a different hardware
like this at this moment (maybe future) and I don't see current framework
block us on reusing these code.

>
> Maybe I'm repeating myself here, the biggest question I'm asking
> region, how much of this code can be reused? I would hope that all of
> it could except for the fpga manager/bridge drivers. That was the
> intent of the original FPGA framework.

I fully understand that it's better to make code reusable, this is what
we are trying to do in these patchsets. But it's still not very clear that
who others or which products will use this DFL framework and how, what's
the difficulty they will face and what kind of help or changes we could
provide. so maybe we could defer some work when people really need it, if
there is no hard blocking issue on architecture level.

> I'll have more time next week to look in more depth.

Looking forward to your feedback. Thanks again for your review. :)

Hao

>
> Alan
>

2018-05-07 21:11:24

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 04/28] fpga: mgr: add compat_id support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

Looks good!

> This patch introduces compat_id support to fpga manager, it adds
> a fpga_compat_id pointer to fpga manager data structure to allow
> fpga manager drivers to save the compatibility id. This compat_id
> could be used for compatibility checking before doing partial
> reconfiguration to associated fpga regions.
>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> include/linux/fpga/fpga-mgr.h | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h
> index 802eac8..f163f22 100644
> --- a/include/linux/fpga/fpga-mgr.h
> +++ b/include/linux/fpga/fpga-mgr.h
> @@ -147,11 +147,23 @@ struct fpga_manager_ops {
> #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR BIT(4)
>
> /**
> + * struct fpga_compat_id - id for compatibility check
> + *
> + * @id_h: high 64bit of the compat_id
> + * @id_l: low 64bit of the compat_id
> + */
> +struct fpga_compat_id {
> + u64 id_h;
> + u64 id_l;
> +};
> +
> +/**
> * struct fpga_manager - fpga manager structure
> * @name: name of low level fpga manager
> * @dev: fpga manager device
> * @ref_mutex: only allows one reference to fpga manager
> * @state: state of fpga manager
> + * @compat_id: FPGA manager id for compatibility check.
> * @mops: pointer to struct of fpga manager ops
> * @priv: low level driver private date
> */
> @@ -160,6 +172,7 @@ struct fpga_manager {
> struct device dev;
> struct mutex ref_mutex;
> enum fpga_mgr_states state;
> + struct fpga_compat_id *compat_id;
> const struct fpga_manager_ops *mops;
> void *priv;
> };
> --
> 1.8.3.1
>

2018-05-07 21:11:53

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 05/28] fpga: region: add compat_id support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

Thanks for making the requested changes. Looks good.

> This patch introduces a compat_id pointer member and sysfs interface
> for each fpga region, similar as compat_id for fpga manager, it allows
> applications to read the per region compat_id for compatibility
> checking before other actions on this fpga-region (e.g PR).
>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> v5: use pointer for compat_id as it's optional to implement.
> ---
> Documentation/ABI/testing/sysfs-class-fpga-region | 9 +++++++++
> drivers/fpga/fpga-region.c | 22 ++++++++++++++++++++++
> include/linux/fpga/fpga-region.h | 2 ++
> 3 files changed, 33 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-class-fpga-region
>
> diff --git a/Documentation/ABI/testing/sysfs-class-fpga-region b/Documentation/ABI/testing/sysfs-class-fpga-region
> new file mode 100644
> index 0000000..9618f74
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-class-fpga-region
> @@ -0,0 +1,9 @@
> +What: /sys/class/fpga_region/<region>/compat_id
> +Date: May 2018
> +KernelVersion: 4.17
> +Contact: Wu Hao <[email protected]>
> +Description: FPGA region id for compatibility check, e.g compatibility
> + of the FPGA reconfiguration hardware and image. This value
> + is defined or calculated by the layer that is creating the
> + FPGA region. This interface returns the compat_id value or
> + just error code -ENOENT in case compat_id is not used.
> diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c
> index d6a9dd2..6929178 100644
> --- a/drivers/fpga/fpga-region.c
> +++ b/drivers/fpga/fpga-region.c
> @@ -162,6 +162,27 @@ int fpga_region_program_fpga(struct fpga_region *region)
> }
> EXPORT_SYMBOL_GPL(fpga_region_program_fpga);
>
> +static ssize_t compat_id_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct fpga_region *region = to_fpga_region(dev);
> +
> + if (!region->compat_id)
> + return -ENOENT;
> +
> + return sprintf(buf, "%016llx%016llx\n",
> + (unsigned long long)region->compat_id->id_h,
> + (unsigned long long)region->compat_id->id_l);
> +}
> +
> +static DEVICE_ATTR_RO(compat_id);
> +
> +static struct attribute *fpga_region_attrs[] = {
> + &dev_attr_compat_id.attr,
> + NULL,
> +};
> +ATTRIBUTE_GROUPS(fpga_region);
> +
> /**
> * fpga_region_create - alloc and init a struct fpga_region
> * @dev: device parent
> @@ -262,6 +283,7 @@ static int __init fpga_region_init(void)
> if (IS_ERR(fpga_region_class))
> return PTR_ERR(fpga_region_class);
>
> + fpga_region_class->dev_groups = fpga_region_groups;
> fpga_region_class->dev_release = fpga_region_dev_release;
>
> return 0;
> diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h
> index f2e215b..e8ad979 100644
> --- a/include/linux/fpga/fpga-region.h
> +++ b/include/linux/fpga/fpga-region.h
> @@ -12,6 +12,7 @@
> * @bridge_list: list of FPGA bridges specified in region
> * @mgr: FPGA manager
> * @info: FPGA image info
> + * @compat_id: FPGA region id for compatibility check.
> * @priv: private data
> * @get_bridges: optional function to get bridges to a list
> */
> @@ -21,6 +22,7 @@ struct fpga_region {
> struct list_head bridge_list;
> struct fpga_manager *mgr;
> struct fpga_image_info *info;
> + struct fpga_compat_id *compat_id;
> void *priv;
> int (*get_bridges)(struct fpga_region *region);
> };
> --
> 1.8.3.1
>

2018-05-07 21:14:19

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 22/28] fpga: dfl: fme-region: add support for compat_id

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> This patch adds compat_id support, it reuses fme manager's
> compat id, as the per region compat id is actually from the
> fme manager's register.
>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> v5: reuse fme manager's compat_id as per region compat_id
> ---
> drivers/fpga/dfl-fme-region.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c
> index 696b313..b82a381 100644
> --- a/drivers/fpga/dfl-fme-region.c
> +++ b/drivers/fpga/dfl-fme-region.c
> @@ -45,6 +45,7 @@ static int fme_region_probe(struct platform_device *pdev)
> }
>
> region->priv = pdata;
> + region->compat_id = mgr->compat_id;
> platform_set_drvdata(pdev, region);
>
> ret = fpga_region_register(region);
> --
> 1.8.3.1
>

2018-05-07 21:14:30

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 19/28] fpga: dfl: fme-mgr: add compat_id support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> This patch adds compat_id support to fme manager driver, it
> reads the ID from the hardware register. And it could be used
> for compatibility check before partial reconfiguration.
>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> drivers/fpga/dfl-fme-mgr.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> index 1c5bc5a..afcdb39 100644
> --- a/drivers/fpga/dfl-fme-mgr.c
> +++ b/drivers/fpga/dfl-fme-mgr.c
> @@ -272,9 +272,17 @@ static u64 fme_mgr_status(struct fpga_manager *mgr)
> .status = fme_mgr_status,
> };
>
> +static void fme_mgr_get_compat_id(void __iomem *fme_pr,
> + struct fpga_compat_id *id)
> +{
> + id->id_l = readq(fme_pr + FME_PR_INTFC_ID_L);
> + id->id_h = readq(fme_pr + FME_PR_INTFC_ID_H);
> +}
> +
> static int fme_mgr_probe(struct platform_device *pdev)
> {
> struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
> + struct fpga_compat_id *compat_id;
> struct device *dev = &pdev->dev;
> struct fme_mgr_priv *priv;
> struct fpga_manager *mgr;
> @@ -295,11 +303,18 @@ static int fme_mgr_probe(struct platform_device *pdev)
> return PTR_ERR(priv->ioaddr);
> }
>
> + compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL);
> + if (!compat_id)
> + return -ENOMEM;
> +
> + fme_mgr_get_compat_id(priv->ioaddr, compat_id);
> +
> mgr = fpga_mgr_create(dev, "DFL FME FPGA Manager",
> &fme_mgr_ops, priv);
> if (!mgr)
> return -ENOMEM;
>
> + mgr->compat_id = compat_id;
> platform_set_drvdata(pdev, mgr);
>
> ret = fpga_mgr_register(mgr);
> --
> 1.8.3.1
>

2018-05-21 03:14:19

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 04/28] fpga: mgr: add compat_id support

On Mon, May 07, 2018 at 04:09:06PM -0500, Alan Tull wrote:
> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>
> Hi Hao,
>
> Looks good!
>
> > This patch introduces compat_id support to fpga manager, it adds
> > a fpga_compat_id pointer to fpga manager data structure to allow
> > fpga manager drivers to save the compatibility id. This compat_id
> > could be used for compatibility checking before doing partial
> > reconfiguration to associated fpga regions.
> >
> > Signed-off-by: Wu Hao <[email protected]>
> Acked-by: Alan Tull <[email protected]>

Hi Alan

Thanks a lot for the acked-by.

Did you get a chance to look into other patches?

Thanks
Hao

2018-05-21 17:34:16

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 04/28] fpga: mgr: add compat_id support

On Sun, May 20, 2018 at 10:03 PM, Wu Hao <[email protected]> wrote:
> On Mon, May 07, 2018 at 04:09:06PM -0500, Alan Tull wrote:
>> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>>
>> Hi Hao,
>>
>> Looks good!
>>
>> > This patch introduces compat_id support to fpga manager, it adds
>> > a fpga_compat_id pointer to fpga manager data structure to allow
>> > fpga manager drivers to save the compatibility id. This compat_id
>> > could be used for compatibility checking before doing partial
>> > reconfiguration to associated fpga regions.
>> >
>> > Signed-off-by: Wu Hao <[email protected]>
>> Acked-by: Alan Tull <[email protected]>
>
> Hi Alan
>
> Thanks a lot for the acked-by.
>
> Did you get a chance to look into other patches?

What I'm looking for mostly is: is it clear that this code was written
to be reused. What do you think? Was it? Is there a way that intent
could be made clear in the code? This patchset has a history of being
a one-off solution for a single platform, doing things to work around
the FPGA framework instead of doing what the framework was intended to
do. The FPGA framework was written so that any FPGA could be used
with interface. Currently in the upstream that means any of the
supported FPGAs can be programmed with the same of-fpga-region code.
It didn't have to get rewritten for each fpga.

This patchset adds 5000 lines and I understand that another 4000 is
coming to add to this. Has that been written so that the upper layer
can be reused? Or will the 'reusable' version be another huge
patchset? Do you see my point? Up to this point I've been trying to
help figure out what changes could make this reusable. If you could
get with someone to take responsibility for architecting this patchset
to be clearly reusable, that could speed things up.

If there are improvements to the current FPGA framework that can help
this work, I'm interested and open to suggestions/code in that
direction..

Alan

>
> Thanks
> Hao

2018-05-22 09:51:11

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 04/28] fpga: mgr: add compat_id support

On Mon, May 21, 2018 at 12:33:05PM -0500, Alan Tull wrote:
> On Sun, May 20, 2018 at 10:03 PM, Wu Hao <[email protected]> wrote:
> > On Mon, May 07, 2018 at 04:09:06PM -0500, Alan Tull wrote:
> >> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
> >>
> >> Hi Hao,
> >>
> >> Looks good!
> >>
> >> > This patch introduces compat_id support to fpga manager, it adds
> >> > a fpga_compat_id pointer to fpga manager data structure to allow
> >> > fpga manager drivers to save the compatibility id. This compat_id
> >> > could be used for compatibility checking before doing partial
> >> > reconfiguration to associated fpga regions.
> >> >
> >> > Signed-off-by: Wu Hao <[email protected]>
> >> Acked-by: Alan Tull <[email protected]>
> >
> > Hi Alan
> >
> > Thanks a lot for the acked-by.
> >
> > Did you get a chance to look into other patches?
>
> What I'm looking for mostly is: is it clear that this code was written
> to be reused. What do you think? Was it? Is there a way that intent
> could be made clear in the code?

Hi Alan,

I am a little confused, so I guess I need to clarify several things here
to avoid misunderstanding.

Actualy we are submitting this patchset to enable Intel FPGA products
(e.g Intel Programmable Acceleration Card (PAC) with Intel Arria 10 GX
FPGA[1]). Once this device driver is accepted by upstream, then people
could use them with standard linux kernel. So the major purpose of the
patchset is just a device driver for Intel specific FPGA device enabling.

The Device Feature List (DFL) is one concept used in the Intel PAC A10
FPGA device, it's a linked list of device features with predefined data
structures. The DFL is defined in the FPGA device spec from Intel, but we
all think it would be great if more people coud reuse it in their devices
too, so some code could be reused. I think it may not be a problem for
some of other Intel FPGA products to reuse these driver code, because they
probably follow the same DFL spec to have Port and FME implemented. It's
the same for other developers/vendors to reuse DFL to create their own
products. They have to implement the Port and FME in device memory per
spec, otherwise it may not be able to reuse current patchset. Please note
that current DFL spec doesn't provide a method to customize the Port or
FME, or even have a totally new Port or FME, but it's possible in the
future version of DFL.

So to me, it's conditional reusable for current patchset. It requires the
FPGA device to follow the same DFL spec with Port and FME defined (and
their private features too).

> This patchset has a history of being
> a one-off solution for a single platform, doing things to work around
> the FPGA framework instead of doing what the framework was intended to
> do. The FPGA framework was written so that any FPGA could be used
> with interface. Currently in the upstream that means any of the
> supported FPGAs can be programmed with the same of-fpga-region code.
> It didn't have to get rewritten for each fpga.

Per your suggestion, we already have a separated layer for enumeration,
different platform devices/drivers for different functional blocks (e.g
Port and FME), it also creates fpga region, manager, bridge to keep aligned
with current FPGA framework. Thanks again for your valuable suggestions.

>
> This patchset adds 5000 lines and I understand that another 4000 is
> coming to add to this.

This is because there are a lot of features implemented by Intel FPGA
products (e.g Intel PAC Card). Most code after this patchset is to add
private features support to Port and FME.

> Has that been written so that the upper layer
> can be reused? Or will the 'reusable' version be another huge
> patchset? Do you see my point?

I think current code is conditional reusable per explaination above, do
you agree? or you have other concerns on current driver architecture?

> Up to this point I've been trying to
> help figure out what changes could make this reusable. If you could
> get with someone to take responsibility for architecting this patchset
> to be clearly reusable, that could speed things up.

Please let me know which part is unclear to you. I see you use "clear"
for several times in your response, I do feel you have some concerns
somewhere, may I know which part is not clear to you? If you're not clear
about how DFL could support the customization. e.g if someone introduced
a totally new Port, then how it's going to reuse our current driver. I
would like to say, the behavior is not defined by DFL spec at all, so
current code may not be able to reused at all. Actually there are a lot
of similar open questions which are also unclear to me (e.g if spec will
allow to introduce a totally new port, if yes, then how to handle the
reset code with existing FME) as spec says nothing about these cases, at
least for current version. As no related description in DFL spec, then
we don't have to consider these cases for now.

Thanks
Hao

>
> If there are improvements to the current FPGA framework that can help
> this work, I'm interested and open to suggestions/code in that
> direction..
>
> Alan
>
> >
> > Thanks
> > Hao

2018-05-23 15:18:56

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 20/28] fpga: dfl: add fpga bridge platform driver for FME

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> This patch adds fpga bridge platform driver for FPGA Management Engine.
> It implements the enable_set callback for fpga bridge.
>
> Signed-off-by: Tim Whisonant <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
> Acked-by: Alan Tull <[email protected]>
> Acked-by: Moritz Fischer <[email protected]>
> ---
> v3: rename driver to fpga-dfl-fme-br
> remove useless dev_dbg in probe function.
> rebased due to fpga api change.
> v4: rename to dfl-fme-br and fix SPDX license issue
> include dfl-fme-pr.h instead of dfl-fme.h
> add Acked-by from Alan and Moritz
> v5: rebase due to API changes.
> defer port and its ops finding when really need.
> ---
> drivers/fpga/Kconfig | 6 +++
> drivers/fpga/Makefile | 1 +
> drivers/fpga/dfl-fme-br.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 121 insertions(+)
> create mode 100644 drivers/fpga/dfl-fme-br.c
>
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 89f76e8..a8f939a 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -156,6 +156,12 @@ config FPGA_DFL_FME_MGR
> help
> Say Y to enable FPGA Manager driver for FPGA Management Engine.
>
> +config FPGA_DFL_FME_BRIDGE
> + tristate "FPGA DFL FME Bridge Driver"
> + depends on FPGA_DFL_FME
> + help
> + Say Y to enable FPGA Bridge driver for FPGA Management Engine.
> +
> config FPGA_DFL_PCI
> tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
> depends on PCI && FPGA_DFL
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index f82814a..75096e9 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> obj-$(CONFIG_FPGA_DFL) += dfl.o
> obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
> obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
> +obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
>
> dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
>
> diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
> new file mode 100644
> index 0000000..5c51b08
> --- /dev/null
> +++ b/drivers/fpga/dfl-fme-br.c
> @@ -0,0 +1,114 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * FPGA Bridge Driver for FPGA Management Engine (FME)
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + * Wu Hao <[email protected]>
> + * Joseph Grecco <[email protected]>
> + * Enno Luebbers <[email protected]>
> + * Tim Whisonant <[email protected]>
> + * Ananda Ravuri <[email protected]>
> + * Henry Mitchel <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/fpga/fpga-bridge.h>
> +
> +#include "dfl.h"
> +#include "dfl-fme-pr.h"
> +
> +struct fme_br_priv {
> + struct dfl_fme_br_pdata *pdata;
> + struct dfl_fpga_port_ops *port_ops;
> + struct platform_device *port_pdev;
> +};
> +
> +static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
> +{
> + struct fme_br_priv *priv = bridge->priv;
> + struct platform_device *port_pdev;
> + struct dfl_fpga_port_ops *ops;
> +
> + if (!priv->port_pdev) {
> + port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
> + &priv->pdata->port_id,
> + dfl_fpga_check_port_id);
> + if (!port_pdev)
> + return -ENODEV;
> +
> + priv->port_pdev = port_pdev;
> + }
> +
> + if (priv->port_pdev && !priv->port_ops) {
> + ops = dfl_fpga_get_port_ops(priv->port_pdev);
> + if (!ops || !ops->enable_set)
> + return -ENOENT;
> +
> + priv->port_ops = ops;
> + }

This is saving some pointers. Is it possible that the port_pdev or
port_ops could go away?

Also, the port ops routines probably should be named
dfl_fpga_port_ops_get/find_port/etc

Alan

> +
> + return priv->port_ops->enable_set(priv->port_pdev, enable);
> +}
> +
> +static const struct fpga_bridge_ops fme_bridge_ops = {
> + .enable_set = fme_bridge_enable_set,
> +};
> +
> +static int fme_br_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct fme_br_priv *priv;
> + struct fpga_bridge *br;
> + int ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + priv->pdata = dev_get_platdata(dev);
> +
> + br = fpga_bridge_create(dev, "DFL FPGA FME Bridge",
> + &fme_bridge_ops, priv);
> + if (!br)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, br);
> +
> + ret = fpga_bridge_register(br);
> + if (ret)
> + fpga_bridge_free(br);
> +
> + return ret;
> +}
> +
> +static int fme_br_remove(struct platform_device *pdev)
> +{
> + struct fpga_bridge *br = platform_get_drvdata(pdev);
> + struct fme_br_priv *priv = br->priv;
> +
> + fpga_bridge_unregister(br);
> +
> + if (priv->port_pdev)
> + put_device(&priv->port_pdev->dev);
> + if (priv->port_ops)
> + dfl_fpga_put_port_ops(priv->port_ops);
> +
> + return 0;
> +}
> +
> +static struct platform_driver fme_br_driver = {
> + .driver = {
> + .name = DFL_FPGA_FME_BRIDGE,
> + },
> + .probe = fme_br_probe,
> + .remove = fme_br_remove,
> +};
> +
> +module_platform_driver(fme_br_driver);
> +
> +MODULE_DESCRIPTION("FPGA Bridge for DFL FPGA Management Engine");
> +MODULE_AUTHOR("Intel Corporation");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dfl-fme-bridge");
> --
> 1.8.3.1
>

2018-05-23 15:40:40

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 20/28] fpga: dfl: add fpga bridge platform driver for FME

On Wed, May 23, 2018 at 10:15:00AM -0500, Alan Tull wrote:
> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>
> Hi Hao,
>
> > This patch adds fpga bridge platform driver for FPGA Management Engine.
> > It implements the enable_set callback for fpga bridge.
> >
> > Signed-off-by: Tim Whisonant <[email protected]>
> > Signed-off-by: Enno Luebbers <[email protected]>
> > Signed-off-by: Shiva Rao <[email protected]>
> > Signed-off-by: Christopher Rauer <[email protected]>
> > Signed-off-by: Wu Hao <[email protected]>
> > Acked-by: Alan Tull <[email protected]>
> > Acked-by: Moritz Fischer <[email protected]>
> > ---
> > v3: rename driver to fpga-dfl-fme-br
> > remove useless dev_dbg in probe function.
> > rebased due to fpga api change.
> > v4: rename to dfl-fme-br and fix SPDX license issue
> > include dfl-fme-pr.h instead of dfl-fme.h
> > add Acked-by from Alan and Moritz
> > v5: rebase due to API changes.
> > defer port and its ops finding when really need.
> > ---
> > drivers/fpga/Kconfig | 6 +++
> > drivers/fpga/Makefile | 1 +
> > drivers/fpga/dfl-fme-br.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 121 insertions(+)
> > create mode 100644 drivers/fpga/dfl-fme-br.c
> >
> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> > index 89f76e8..a8f939a 100644
> > --- a/drivers/fpga/Kconfig
> > +++ b/drivers/fpga/Kconfig
> > @@ -156,6 +156,12 @@ config FPGA_DFL_FME_MGR
> > help
> > Say Y to enable FPGA Manager driver for FPGA Management Engine.
> >
> > +config FPGA_DFL_FME_BRIDGE
> > + tristate "FPGA DFL FME Bridge Driver"
> > + depends on FPGA_DFL_FME
> > + help
> > + Say Y to enable FPGA Bridge driver for FPGA Management Engine.
> > +
> > config FPGA_DFL_PCI
> > tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
> > depends on PCI && FPGA_DFL
> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> > index f82814a..75096e9 100644
> > --- a/drivers/fpga/Makefile
> > +++ b/drivers/fpga/Makefile
> > @@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> > obj-$(CONFIG_FPGA_DFL) += dfl.o
> > obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
> > obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
> > +obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
> >
> > dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
> >
> > diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
> > new file mode 100644
> > index 0000000..5c51b08
> > --- /dev/null
> > +++ b/drivers/fpga/dfl-fme-br.c
> > @@ -0,0 +1,114 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * FPGA Bridge Driver for FPGA Management Engine (FME)
> > + *
> > + * Copyright (C) 2017 Intel Corporation, Inc.
> > + *
> > + * Authors:
> > + * Wu Hao <[email protected]>
> > + * Joseph Grecco <[email protected]>
> > + * Enno Luebbers <[email protected]>
> > + * Tim Whisonant <[email protected]>
> > + * Ananda Ravuri <[email protected]>
> > + * Henry Mitchel <[email protected]>
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/fpga/fpga-bridge.h>
> > +
> > +#include "dfl.h"
> > +#include "dfl-fme-pr.h"
> > +
> > +struct fme_br_priv {
> > + struct dfl_fme_br_pdata *pdata;
> > + struct dfl_fpga_port_ops *port_ops;
> > + struct platform_device *port_pdev;
> > +};
> > +
> > +static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
> > +{
> > + struct fme_br_priv *priv = bridge->priv;
> > + struct platform_device *port_pdev;
> > + struct dfl_fpga_port_ops *ops;
> > +
> > + if (!priv->port_pdev) {
> > + port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
> > + &priv->pdata->port_id,
> > + dfl_fpga_check_port_id);
> > + if (!port_pdev)
> > + return -ENODEV;
> > +
> > + priv->port_pdev = port_pdev;
> > + }
> > +
> > + if (priv->port_pdev && !priv->port_ops) {
> > + ops = dfl_fpga_get_port_ops(priv->port_pdev);
> > + if (!ops || !ops->enable_set)
> > + return -ENOENT;
> > +
> > + priv->port_ops = ops;
> > + }
>
> This is saving some pointers. Is it possible that the port_pdev or
> port_ops could go away?

Hi Alan

Thanks for the comments.

The find_port function will get the port device to prevent that.
You can see put device in remove function. And it's similar for the
port ops. In dfl_fpga_get_port_ops function, it will prevent unexpected
port ops removing by try module get.

>
> Also, the port ops routines probably should be named
> dfl_fpga_port_ops_get/find_port/etc
>

Hm.. as I see there are functions named as get_device(), put_device(), so
I just name it as ..._get_port_ops and ..._put_port_ops in similar way. :)
If you think that could be a better name, it's fine for me to change them.

Thanks
Hao

> Alan
>
> > +
> > + return priv->port_ops->enable_set(priv->port_pdev, enable);
> > +}
> > +
> > +static const struct fpga_bridge_ops fme_bridge_ops = {
> > + .enable_set = fme_bridge_enable_set,
> > +};
> > +
> > +static int fme_br_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct fme_br_priv *priv;
> > + struct fpga_bridge *br;
> > + int ret;
> > +
> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > +
> > + priv->pdata = dev_get_platdata(dev);
> > +
> > + br = fpga_bridge_create(dev, "DFL FPGA FME Bridge",
> > + &fme_bridge_ops, priv);
> > + if (!br)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, br);
> > +
> > + ret = fpga_bridge_register(br);
> > + if (ret)
> > + fpga_bridge_free(br);
> > +
> > + return ret;
> > +}
> > +
> > +static int fme_br_remove(struct platform_device *pdev)
> > +{
> > + struct fpga_bridge *br = platform_get_drvdata(pdev);
> > + struct fme_br_priv *priv = br->priv;
> > +
> > + fpga_bridge_unregister(br);
> > +
> > + if (priv->port_pdev)
> > + put_device(&priv->port_pdev->dev);
> > + if (priv->port_ops)
> > + dfl_fpga_put_port_ops(priv->port_ops);
> > +
> > + return 0;
> > +}
> > +
> > +static struct platform_driver fme_br_driver = {
> > + .driver = {
> > + .name = DFL_FPGA_FME_BRIDGE,
> > + },
> > + .probe = fme_br_probe,
> > + .remove = fme_br_remove,
> > +};
> > +
> > +module_platform_driver(fme_br_driver);
> > +
> > +MODULE_DESCRIPTION("FPGA Bridge for DFL FPGA Management Engine");
> > +MODULE_AUTHOR("Intel Corporation");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_ALIAS("platform:dfl-fme-bridge");
> > --
> > 1.8.3.1
> >

2018-05-23 21:08:01

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 20/28] fpga: dfl: add fpga bridge platform driver for FME

On Wed, May 23, 2018 at 10:28 AM, Wu Hao <[email protected]> wrote:
> On Wed, May 23, 2018 at 10:15:00AM -0500, Alan Tull wrote:
>> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>>
>> Hi Hao,
>>
>> > This patch adds fpga bridge platform driver for FPGA Management Engine.
>> > It implements the enable_set callback for fpga bridge.
>> >
>> > Signed-off-by: Tim Whisonant <[email protected]>
>> > Signed-off-by: Enno Luebbers <[email protected]>
>> > Signed-off-by: Shiva Rao <[email protected]>
>> > Signed-off-by: Christopher Rauer <[email protected]>
>> > Signed-off-by: Wu Hao <[email protected]>
>> > Acked-by: Alan Tull <[email protected]>
>> > Acked-by: Moritz Fischer <[email protected]>
>> > ---
>> > v3: rename driver to fpga-dfl-fme-br
>> > remove useless dev_dbg in probe function.
>> > rebased due to fpga api change.
>> > v4: rename to dfl-fme-br and fix SPDX license issue
>> > include dfl-fme-pr.h instead of dfl-fme.h
>> > add Acked-by from Alan and Moritz
>> > v5: rebase due to API changes.
>> > defer port and its ops finding when really need.
>> > ---
>> > drivers/fpga/Kconfig | 6 +++
>> > drivers/fpga/Makefile | 1 +
>> > drivers/fpga/dfl-fme-br.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
>> > 3 files changed, 121 insertions(+)
>> > create mode 100644 drivers/fpga/dfl-fme-br.c
>> >
>> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
>> > index 89f76e8..a8f939a 100644
>> > --- a/drivers/fpga/Kconfig
>> > +++ b/drivers/fpga/Kconfig
>> > @@ -156,6 +156,12 @@ config FPGA_DFL_FME_MGR
>> > help
>> > Say Y to enable FPGA Manager driver for FPGA Management Engine.
>> >
>> > +config FPGA_DFL_FME_BRIDGE
>> > + tristate "FPGA DFL FME Bridge Driver"
>> > + depends on FPGA_DFL_FME
>> > + help
>> > + Say Y to enable FPGA Bridge driver for FPGA Management Engine.
>> > +
>> > config FPGA_DFL_PCI
>> > tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
>> > depends on PCI && FPGA_DFL
>> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
>> > index f82814a..75096e9 100644
>> > --- a/drivers/fpga/Makefile
>> > +++ b/drivers/fpga/Makefile
>> > @@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
>> > obj-$(CONFIG_FPGA_DFL) += dfl.o
>> > obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
>> > obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
>> > +obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
>> >
>> > dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
>> >
>> > diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
>> > new file mode 100644
>> > index 0000000..5c51b08
>> > --- /dev/null
>> > +++ b/drivers/fpga/dfl-fme-br.c
>> > @@ -0,0 +1,114 @@
>> > +// SPDX-License-Identifier: GPL-2.0
>> > +/*
>> > + * FPGA Bridge Driver for FPGA Management Engine (FME)
>> > + *
>> > + * Copyright (C) 2017 Intel Corporation, Inc.
>> > + *
>> > + * Authors:
>> > + * Wu Hao <[email protected]>
>> > + * Joseph Grecco <[email protected]>
>> > + * Enno Luebbers <[email protected]>
>> > + * Tim Whisonant <[email protected]>
>> > + * Ananda Ravuri <[email protected]>
>> > + * Henry Mitchel <[email protected]>
>> > + */
>> > +
>> > +#include <linux/module.h>
>> > +#include <linux/fpga/fpga-bridge.h>
>> > +
>> > +#include "dfl.h"
>> > +#include "dfl-fme-pr.h"
>> > +
>> > +struct fme_br_priv {
>> > + struct dfl_fme_br_pdata *pdata;
>> > + struct dfl_fpga_port_ops *port_ops;
>> > + struct platform_device *port_pdev;
>> > +};
>> > +
>> > +static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
>> > +{
>> > + struct fme_br_priv *priv = bridge->priv;
>> > + struct platform_device *port_pdev;
>> > + struct dfl_fpga_port_ops *ops;
>> > +
>> > + if (!priv->port_pdev) {
>> > + port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
>> > + &priv->pdata->port_id,
>> > + dfl_fpga_check_port_id);
>> > + if (!port_pdev)
>> > + return -ENODEV;
>> > +
>> > + priv->port_pdev = port_pdev;
>> > + }
>> > +
>> > + if (priv->port_pdev && !priv->port_ops) {
>> > + ops = dfl_fpga_get_port_ops(priv->port_pdev);
>> > + if (!ops || !ops->enable_set)
>> > + return -ENOENT;
>> > +
>> > + priv->port_ops = ops;
>> > + }
>>
>> This is saving some pointers. Is it possible that the port_pdev or
>> port_ops could go away?
>
> Hi Alan
>
> Thanks for the comments.
>
> The find_port function will get the port device to prevent that.
> You can see put device in remove function. And it's similar for the
> port ops. In dfl_fpga_get_port_ops function, it will prevent unexpected
> port ops removing by try module get.

OK, good, and I see the find_port function documents that put_device
is needed, so that's good too.

When we previously discussed this [1] you described a procedure of
hot-unplugging the VF AFU from the VM and turning it back to PF before
doing the FPGA programming. Some comments on that would be helpful,
maybe located with the port_ops functions.

[1] https://lkml.org/lkml/2018/4/6/180

>
>>
>> Also, the port ops routines probably should be named
>> dfl_fpga_port_ops_get/find_port/etc
>>
>
> Hm.. as I see there are functions named as get_device(), put_device(), so
> I just name it as ..._get_port_ops and ..._put_port_ops in similar way. :)
> If you think that could be a better name, it's fine for me to change them.

Yes, I've seen that too. I think keeping the prefix the same helps to
organize the namespace. Plus :) if I'm grepping through the code, I
can be lazy and not have to use a regex to find the port ops
functions.

Alan

>
> Thanks
> Hao
>
>> Alan
>>
>> > +
>> > + return priv->port_ops->enable_set(priv->port_pdev, enable);
>> > +}
>> > +
>> > +static const struct fpga_bridge_ops fme_bridge_ops = {
>> > + .enable_set = fme_bridge_enable_set,
>> > +};
>> > +
>> > +static int fme_br_probe(struct platform_device *pdev)
>> > +{
>> > + struct device *dev = &pdev->dev;
>> > + struct fme_br_priv *priv;
>> > + struct fpga_bridge *br;
>> > + int ret;
>> > +
>> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> > + if (!priv)
>> > + return -ENOMEM;
>> > +
>> > + priv->pdata = dev_get_platdata(dev);
>> > +
>> > + br = fpga_bridge_create(dev, "DFL FPGA FME Bridge",
>> > + &fme_bridge_ops, priv);
>> > + if (!br)
>> > + return -ENOMEM;
>> > +
>> > + platform_set_drvdata(pdev, br);
>> > +
>> > + ret = fpga_bridge_register(br);
>> > + if (ret)
>> > + fpga_bridge_free(br);
>> > +
>> > + return ret;
>> > +}
>> > +
>> > +static int fme_br_remove(struct platform_device *pdev)
>> > +{
>> > + struct fpga_bridge *br = platform_get_drvdata(pdev);
>> > + struct fme_br_priv *priv = br->priv;
>> > +
>> > + fpga_bridge_unregister(br);
>> > +
>> > + if (priv->port_pdev)
>> > + put_device(&priv->port_pdev->dev);
>> > + if (priv->port_ops)
>> > + dfl_fpga_put_port_ops(priv->port_ops);
>> > +
>> > + return 0;
>> > +}
>> > +
>> > +static struct platform_driver fme_br_driver = {
>> > + .driver = {
>> > + .name = DFL_FPGA_FME_BRIDGE,
>> > + },
>> > + .probe = fme_br_probe,
>> > + .remove = fme_br_remove,
>> > +};
>> > +
>> > +module_platform_driver(fme_br_driver);
>> > +
>> > +MODULE_DESCRIPTION("FPGA Bridge for DFL FPGA Management Engine");
>> > +MODULE_AUTHOR("Intel Corporation");
>> > +MODULE_LICENSE("GPL v2");
>> > +MODULE_ALIAS("platform:dfl-fme-bridge");
>> > --
>> > 1.8.3.1
>> >

2018-05-23 23:55:39

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 20/28] fpga: dfl: add fpga bridge platform driver for FME

On Wed, May 23, 2018 at 04:06:17PM -0500, Alan Tull wrote:
> On Wed, May 23, 2018 at 10:28 AM, Wu Hao <[email protected]> wrote:
> > On Wed, May 23, 2018 at 10:15:00AM -0500, Alan Tull wrote:
> >> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
> >>
> >> Hi Hao,
> >>
> >> > This patch adds fpga bridge platform driver for FPGA Management Engine.
> >> > It implements the enable_set callback for fpga bridge.
> >> >
> >> > Signed-off-by: Tim Whisonant <[email protected]>
> >> > Signed-off-by: Enno Luebbers <[email protected]>
> >> > Signed-off-by: Shiva Rao <[email protected]>
> >> > Signed-off-by: Christopher Rauer <[email protected]>
> >> > Signed-off-by: Wu Hao <[email protected]>
> >> > Acked-by: Alan Tull <[email protected]>
> >> > Acked-by: Moritz Fischer <[email protected]>
> >> > ---
> >> > v3: rename driver to fpga-dfl-fme-br
> >> > remove useless dev_dbg in probe function.
> >> > rebased due to fpga api change.
> >> > v4: rename to dfl-fme-br and fix SPDX license issue
> >> > include dfl-fme-pr.h instead of dfl-fme.h
> >> > add Acked-by from Alan and Moritz
> >> > v5: rebase due to API changes.
> >> > defer port and its ops finding when really need.
> >> > ---
> >> > drivers/fpga/Kconfig | 6 +++
> >> > drivers/fpga/Makefile | 1 +
> >> > drivers/fpga/dfl-fme-br.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
> >> > 3 files changed, 121 insertions(+)
> >> > create mode 100644 drivers/fpga/dfl-fme-br.c
> >> >
> >> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> >> > index 89f76e8..a8f939a 100644
> >> > --- a/drivers/fpga/Kconfig
> >> > +++ b/drivers/fpga/Kconfig
> >> > @@ -156,6 +156,12 @@ config FPGA_DFL_FME_MGR
> >> > help
> >> > Say Y to enable FPGA Manager driver for FPGA Management Engine.
> >> >
> >> > +config FPGA_DFL_FME_BRIDGE
> >> > + tristate "FPGA DFL FME Bridge Driver"
> >> > + depends on FPGA_DFL_FME
> >> > + help
> >> > + Say Y to enable FPGA Bridge driver for FPGA Management Engine.
> >> > +
> >> > config FPGA_DFL_PCI
> >> > tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
> >> > depends on PCI && FPGA_DFL
> >> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> >> > index f82814a..75096e9 100644
> >> > --- a/drivers/fpga/Makefile
> >> > +++ b/drivers/fpga/Makefile
> >> > @@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> >> > obj-$(CONFIG_FPGA_DFL) += dfl.o
> >> > obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
> >> > obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
> >> > +obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
> >> >
> >> > dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
> >> >
> >> > diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
> >> > new file mode 100644
> >> > index 0000000..5c51b08
> >> > --- /dev/null
> >> > +++ b/drivers/fpga/dfl-fme-br.c
> >> > @@ -0,0 +1,114 @@
> >> > +// SPDX-License-Identifier: GPL-2.0
> >> > +/*
> >> > + * FPGA Bridge Driver for FPGA Management Engine (FME)
> >> > + *
> >> > + * Copyright (C) 2017 Intel Corporation, Inc.
> >> > + *
> >> > + * Authors:
> >> > + * Wu Hao <[email protected]>
> >> > + * Joseph Grecco <[email protected]>
> >> > + * Enno Luebbers <[email protected]>
> >> > + * Tim Whisonant <[email protected]>
> >> > + * Ananda Ravuri <[email protected]>
> >> > + * Henry Mitchel <[email protected]>
> >> > + */
> >> > +
> >> > +#include <linux/module.h>
> >> > +#include <linux/fpga/fpga-bridge.h>
> >> > +
> >> > +#include "dfl.h"
> >> > +#include "dfl-fme-pr.h"
> >> > +
> >> > +struct fme_br_priv {
> >> > + struct dfl_fme_br_pdata *pdata;
> >> > + struct dfl_fpga_port_ops *port_ops;
> >> > + struct platform_device *port_pdev;
> >> > +};
> >> > +
> >> > +static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
> >> > +{
> >> > + struct fme_br_priv *priv = bridge->priv;
> >> > + struct platform_device *port_pdev;
> >> > + struct dfl_fpga_port_ops *ops;
> >> > +
> >> > + if (!priv->port_pdev) {
> >> > + port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
> >> > + &priv->pdata->port_id,
> >> > + dfl_fpga_check_port_id);
> >> > + if (!port_pdev)
> >> > + return -ENODEV;
> >> > +
> >> > + priv->port_pdev = port_pdev;
> >> > + }
> >> > +
> >> > + if (priv->port_pdev && !priv->port_ops) {
> >> > + ops = dfl_fpga_get_port_ops(priv->port_pdev);
> >> > + if (!ops || !ops->enable_set)
> >> > + return -ENOENT;
> >> > +
> >> > + priv->port_ops = ops;
> >> > + }
> >>
> >> This is saving some pointers. Is it possible that the port_pdev or
> >> port_ops could go away?
> >
> > Hi Alan
> >
> > Thanks for the comments.
> >
> > The find_port function will get the port device to prevent that.
> > You can see put device in remove function. And it's similar for the
> > port ops. In dfl_fpga_get_port_ops function, it will prevent unexpected
> > port ops removing by try module get.
>
> OK, good, and I see the find_port function documents that put_device
> is needed, so that's good too.
>
> When we previously discussed this [1] you described a procedure of
> hot-unplugging the VF AFU from the VM and turning it back to PF before
> doing the FPGA programming. Some comments on that would be helpful,
> maybe located with the port_ops functions.
>

Hi Alan

Actually I plan to add those descriptions about virtualization in the
documenation/fpga/dfl.txt, together with the patch which enables the
virtualization support for Intel FPGA device (e.g Intel PAC card). I
think dfl.txt is a better place to have that procedure documented.
How do you think?


> [1] https://lkml.org/lkml/2018/4/6/180
>
> >
> >>
> >> Also, the port ops routines probably should be named
> >> dfl_fpga_port_ops_get/find_port/etc
> >>
> >
> > Hm.. as I see there are functions named as get_device(), put_device(), so
> > I just name it as ..._get_port_ops and ..._put_port_ops in similar way. :)
> > If you think that could be a better name, it's fine for me to change them.
>
> Yes, I've seen that too. I think keeping the prefix the same helps to
> organize the namespace. Plus :) if I'm grepping through the code, I
> can be lazy and not have to use a regex to find the port ops
> functions.

so we good with current naming?

some places we find it's get_device() and put_device(), but other functions
like device_add(), device_del(). actually it's not a big problem I think, :)
currently port_ops has a unified style ..._add/del/get/put_port_ops. I am
fine with both kind of naming, if you prefer ...port_ops_add/del/get/put
style, please let me know, I can change them in the next version.

Thanks
Hao

>
> Alan
>
> >
> > Thanks
> > Hao
> >
> >> Alan
> >>
> >> > +
> >> > + return priv->port_ops->enable_set(priv->port_pdev, enable);
> >> > +}
> >> > +
> >> > +static const struct fpga_bridge_ops fme_bridge_ops = {
> >> > + .enable_set = fme_bridge_enable_set,
> >> > +};
> >> > +
> >> > +static int fme_br_probe(struct platform_device *pdev)
> >> > +{
> >> > + struct device *dev = &pdev->dev;
> >> > + struct fme_br_priv *priv;
> >> > + struct fpga_bridge *br;
> >> > + int ret;
> >> > +
> >> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> >> > + if (!priv)
> >> > + return -ENOMEM;
> >> > +
> >> > + priv->pdata = dev_get_platdata(dev);
> >> > +
> >> > + br = fpga_bridge_create(dev, "DFL FPGA FME Bridge",
> >> > + &fme_bridge_ops, priv);
> >> > + if (!br)
> >> > + return -ENOMEM;
> >> > +
> >> > + platform_set_drvdata(pdev, br);
> >> > +
> >> > + ret = fpga_bridge_register(br);
> >> > + if (ret)
> >> > + fpga_bridge_free(br);
> >> > +
> >> > + return ret;
> >> > +}
> >> > +
> >> > +static int fme_br_remove(struct platform_device *pdev)
> >> > +{
> >> > + struct fpga_bridge *br = platform_get_drvdata(pdev);
> >> > + struct fme_br_priv *priv = br->priv;
> >> > +
> >> > + fpga_bridge_unregister(br);
> >> > +
> >> > + if (priv->port_pdev)
> >> > + put_device(&priv->port_pdev->dev);
> >> > + if (priv->port_ops)
> >> > + dfl_fpga_put_port_ops(priv->port_ops);
> >> > +
> >> > + return 0;
> >> > +}
> >> > +
> >> > +static struct platform_driver fme_br_driver = {
> >> > + .driver = {
> >> > + .name = DFL_FPGA_FME_BRIDGE,
> >> > + },
> >> > + .probe = fme_br_probe,
> >> > + .remove = fme_br_remove,
> >> > +};
> >> > +
> >> > +module_platform_driver(fme_br_driver);
> >> > +
> >> > +MODULE_DESCRIPTION("FPGA Bridge for DFL FPGA Management Engine");
> >> > +MODULE_AUTHOR("Intel Corporation");
> >> > +MODULE_LICENSE("GPL v2");
> >> > +MODULE_ALIAS("platform:dfl-fme-bridge");
> >> > --
> >> > 1.8.3.1
> >> >
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2018-05-25 02:34:23

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 20/28] fpga: dfl: add fpga bridge platform driver for FME

On Wed, May 23, 2018 at 6:42 PM, Wu Hao <[email protected]> wrote:
> On Wed, May 23, 2018 at 04:06:17PM -0500, Alan Tull wrote:
>> On Wed, May 23, 2018 at 10:28 AM, Wu Hao <[email protected]> wrote:
>> > On Wed, May 23, 2018 at 10:15:00AM -0500, Alan Tull wrote:
>> >> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>> >>
>> >> Hi Hao,
>> >>
>> >> > This patch adds fpga bridge platform driver for FPGA Management Engine.
>> >> > It implements the enable_set callback for fpga bridge.
>> >> >
>> >> > Signed-off-by: Tim Whisonant <[email protected]>
>> >> > Signed-off-by: Enno Luebbers <[email protected]>
>> >> > Signed-off-by: Shiva Rao <[email protected]>
>> >> > Signed-off-by: Christopher Rauer <[email protected]>
>> >> > Signed-off-by: Wu Hao <[email protected]>
>> >> > Acked-by: Alan Tull <[email protected]>
>> >> > Acked-by: Moritz Fischer <[email protected]>
>> >> > ---
>> >> > v3: rename driver to fpga-dfl-fme-br
>> >> > remove useless dev_dbg in probe function.
>> >> > rebased due to fpga api change.
>> >> > v4: rename to dfl-fme-br and fix SPDX license issue
>> >> > include dfl-fme-pr.h instead of dfl-fme.h
>> >> > add Acked-by from Alan and Moritz
>> >> > v5: rebase due to API changes.
>> >> > defer port and its ops finding when really need.
>> >> > ---
>> >> > drivers/fpga/Kconfig | 6 +++
>> >> > drivers/fpga/Makefile | 1 +
>> >> > drivers/fpga/dfl-fme-br.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
>> >> > 3 files changed, 121 insertions(+)
>> >> > create mode 100644 drivers/fpga/dfl-fme-br.c
>> >> >
>> >> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
>> >> > index 89f76e8..a8f939a 100644
>> >> > --- a/drivers/fpga/Kconfig
>> >> > +++ b/drivers/fpga/Kconfig
>> >> > @@ -156,6 +156,12 @@ config FPGA_DFL_FME_MGR
>> >> > help
>> >> > Say Y to enable FPGA Manager driver for FPGA Management Engine.
>> >> >
>> >> > +config FPGA_DFL_FME_BRIDGE
>> >> > + tristate "FPGA DFL FME Bridge Driver"
>> >> > + depends on FPGA_DFL_FME
>> >> > + help
>> >> > + Say Y to enable FPGA Bridge driver for FPGA Management Engine.
>> >> > +
>> >> > config FPGA_DFL_PCI
>> >> > tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
>> >> > depends on PCI && FPGA_DFL
>> >> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
>> >> > index f82814a..75096e9 100644
>> >> > --- a/drivers/fpga/Makefile
>> >> > +++ b/drivers/fpga/Makefile
>> >> > @@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
>> >> > obj-$(CONFIG_FPGA_DFL) += dfl.o
>> >> > obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
>> >> > obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
>> >> > +obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
>> >> >
>> >> > dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
>> >> >
>> >> > diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
>> >> > new file mode 100644
>> >> > index 0000000..5c51b08
>> >> > --- /dev/null
>> >> > +++ b/drivers/fpga/dfl-fme-br.c
>> >> > @@ -0,0 +1,114 @@
>> >> > +// SPDX-License-Identifier: GPL-2.0
>> >> > +/*
>> >> > + * FPGA Bridge Driver for FPGA Management Engine (FME)
>> >> > + *
>> >> > + * Copyright (C) 2017 Intel Corporation, Inc.
>> >> > + *
>> >> > + * Authors:
>> >> > + * Wu Hao <[email protected]>
>> >> > + * Joseph Grecco <[email protected]>
>> >> > + * Enno Luebbers <[email protected]>
>> >> > + * Tim Whisonant <[email protected]>
>> >> > + * Ananda Ravuri <[email protected]>
>> >> > + * Henry Mitchel <[email protected]>
>> >> > + */
>> >> > +
>> >> > +#include <linux/module.h>
>> >> > +#include <linux/fpga/fpga-bridge.h>
>> >> > +
>> >> > +#include "dfl.h"
>> >> > +#include "dfl-fme-pr.h"
>> >> > +
>> >> > +struct fme_br_priv {
>> >> > + struct dfl_fme_br_pdata *pdata;
>> >> > + struct dfl_fpga_port_ops *port_ops;
>> >> > + struct platform_device *port_pdev;
>> >> > +};
>> >> > +
>> >> > +static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
>> >> > +{
>> >> > + struct fme_br_priv *priv = bridge->priv;
>> >> > + struct platform_device *port_pdev;
>> >> > + struct dfl_fpga_port_ops *ops;
>> >> > +
>> >> > + if (!priv->port_pdev) {
>> >> > + port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
>> >> > + &priv->pdata->port_id,
>> >> > + dfl_fpga_check_port_id);
>> >> > + if (!port_pdev)
>> >> > + return -ENODEV;
>> >> > +
>> >> > + priv->port_pdev = port_pdev;
>> >> > + }
>> >> > +
>> >> > + if (priv->port_pdev && !priv->port_ops) {
>> >> > + ops = dfl_fpga_get_port_ops(priv->port_pdev);
>> >> > + if (!ops || !ops->enable_set)
>> >> > + return -ENOENT;
>> >> > +
>> >> > + priv->port_ops = ops;
>> >> > + }
>> >>
>> >> This is saving some pointers. Is it possible that the port_pdev or
>> >> port_ops could go away?
>> >
>> > Hi Alan
>> >
>> > Thanks for the comments.
>> >
>> > The find_port function will get the port device to prevent that.
>> > You can see put device in remove function. And it's similar for the
>> > port ops. In dfl_fpga_get_port_ops function, it will prevent unexpected
>> > port ops removing by try module get.
>>
>> OK, good, and I see the find_port function documents that put_device
>> is needed, so that's good too.
>>
>> When we previously discussed this [1] you described a procedure of
>> hot-unplugging the VF AFU from the VM and turning it back to PF before
>> doing the FPGA programming. Some comments on that would be helpful,
>> maybe located with the port_ops functions.
>>
>
> Hi Alan
>
> Actually I plan to add those descriptions about virtualization in the
> documenation/fpga/dfl.txt, together with the patch which enables the
> virtualization support for Intel FPGA device (e.g Intel PAC card).

Great

> I
> think dfl.txt is a better place to have that procedure documented.
> How do you think?

If it is only in the dff.txt, it may remain unclear (in the code) that
that is an issue that the code is working around.

>
>
>> [1] https://lkml.org/lkml/2018/4/6/180
>>
>> >
>> >>
>> >> Also, the port ops routines probably should be named
>> >> dfl_fpga_port_ops_get/find_port/etc
>> >>
>> >
>> > Hm.. as I see there are functions named as get_device(), put_device(), so
>> > I just name it as ..._get_port_ops and ..._put_port_ops in similar way. :)
>> > If you think that could be a better name, it's fine for me to change them.
>>
>> Yes, I've seen that too. I think keeping the prefix the same helps to
>> organize the namespace. Plus :) if I'm grepping through the code, I
>> can be lazy and not have to use a regex to find the port ops
>> functions.
>
> so we good with current naming?

Recently GregKH give us some guidance about using a common prefix [2].

>
> some places we find it's get_device() and put_device(), but other functions
> like device_add(), device_del(). actually it's not a big problem I think, :)
> currently port_ops has a unified style ..._add/del/get/put_port_ops. I am
> fine with both kind of naming, if you prefer ...port_ops_add/del/get/put
> style, please let me know, I can change them in the next version.

Since you have struct dfl_fpga_port_ops, you can just add _{action} to
the name of the struct and you have dfl_fpga_port_ops_add/del/get/pu

Thanks,
Alan

[2] https://lkml.org/lkml/2018/3/15/709

>
> Thanks
> Hao
>
>>
>> Alan
>>
>> >
>> > Thanks
>> > Hao
>> >
>> >> Alan
>> >>
>> >> > +
>> >> > + return priv->port_ops->enable_set(priv->port_pdev, enable);
>> >> > +}
>> >> > +
>> >> > +static const struct fpga_bridge_ops fme_bridge_ops = {
>> >> > + .enable_set = fme_bridge_enable_set,
>> >> > +};
>> >> > +
>> >> > +static int fme_br_probe(struct platform_device *pdev)
>> >> > +{
>> >> > + struct device *dev = &pdev->dev;
>> >> > + struct fme_br_priv *priv;
>> >> > + struct fpga_bridge *br;
>> >> > + int ret;
>> >> > +
>> >> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> >> > + if (!priv)
>> >> > + return -ENOMEM;
>> >> > +
>> >> > + priv->pdata = dev_get_platdata(dev);
>> >> > +
>> >> > + br = fpga_bridge_create(dev, "DFL FPGA FME Bridge",
>> >> > + &fme_bridge_ops, priv);
>> >> > + if (!br)
>> >> > + return -ENOMEM;
>> >> > +
>> >> > + platform_set_drvdata(pdev, br);
>> >> > +
>> >> > + ret = fpga_bridge_register(br);
>> >> > + if (ret)
>> >> > + fpga_bridge_free(br);
>> >> > +
>> >> > + return ret;
>> >> > +}
>> >> > +
>> >> > +static int fme_br_remove(struct platform_device *pdev)
>> >> > +{
>> >> > + struct fpga_bridge *br = platform_get_drvdata(pdev);
>> >> > + struct fme_br_priv *priv = br->priv;
>> >> > +
>> >> > + fpga_bridge_unregister(br);
>> >> > +
>> >> > + if (priv->port_pdev)
>> >> > + put_device(&priv->port_pdev->dev);
>> >> > + if (priv->port_ops)
>> >> > + dfl_fpga_put_port_ops(priv->port_ops);
>> >> > +
>> >> > + return 0;
>> >> > +}
>> >> > +
>> >> > +static struct platform_driver fme_br_driver = {
>> >> > + .driver = {
>> >> > + .name = DFL_FPGA_FME_BRIDGE,
>> >> > + },
>> >> > + .probe = fme_br_probe,
>> >> > + .remove = fme_br_remove,
>> >> > +};
>> >> > +
>> >> > +module_platform_driver(fme_br_driver);
>> >> > +
>> >> > +MODULE_DESCRIPTION("FPGA Bridge for DFL FPGA Management Engine");
>> >> > +MODULE_AUTHOR("Intel Corporation");
>> >> > +MODULE_LICENSE("GPL v2");
>> >> > +MODULE_ALIAS("platform:dfl-fme-bridge");
>> >> > --
>> >> > 1.8.3.1
>> >> >
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
>> the body of a message to [email protected]
>> More majordomo info at http://vger.kernel.org/majordomo-info.html

2018-05-25 02:48:48

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 20/28] fpga: dfl: add fpga bridge platform driver for FME

On Thu, May 24, 2018 at 12:26:09PM -0500, Alan Tull wrote:
> On Wed, May 23, 2018 at 6:42 PM, Wu Hao <[email protected]> wrote:
> > On Wed, May 23, 2018 at 04:06:17PM -0500, Alan Tull wrote:
> >> On Wed, May 23, 2018 at 10:28 AM, Wu Hao <[email protected]> wrote:
> >> > On Wed, May 23, 2018 at 10:15:00AM -0500, Alan Tull wrote:
> >> >> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
> >> >>
> >> >> Hi Hao,
> >> >>
> >> >> > This patch adds fpga bridge platform driver for FPGA Management Engine.
> >> >> > It implements the enable_set callback for fpga bridge.
> >> >> >
> >> >> > Signed-off-by: Tim Whisonant <[email protected]>
> >> >> > Signed-off-by: Enno Luebbers <[email protected]>
> >> >> > Signed-off-by: Shiva Rao <[email protected]>
> >> >> > Signed-off-by: Christopher Rauer <[email protected]>
> >> >> > Signed-off-by: Wu Hao <[email protected]>
> >> >> > Acked-by: Alan Tull <[email protected]>
> >> >> > Acked-by: Moritz Fischer <[email protected]>
> >> >> > ---
> >> >> > v3: rename driver to fpga-dfl-fme-br
> >> >> > remove useless dev_dbg in probe function.
> >> >> > rebased due to fpga api change.
> >> >> > v4: rename to dfl-fme-br and fix SPDX license issue
> >> >> > include dfl-fme-pr.h instead of dfl-fme.h
> >> >> > add Acked-by from Alan and Moritz
> >> >> > v5: rebase due to API changes.
> >> >> > defer port and its ops finding when really need.
> >> >> > ---
> >> >> > drivers/fpga/Kconfig | 6 +++
> >> >> > drivers/fpga/Makefile | 1 +
> >> >> > drivers/fpga/dfl-fme-br.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++
> >> >> > 3 files changed, 121 insertions(+)
> >> >> > create mode 100644 drivers/fpga/dfl-fme-br.c
> >> >> >
> >> >> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> >> >> > index 89f76e8..a8f939a 100644
> >> >> > --- a/drivers/fpga/Kconfig
> >> >> > +++ b/drivers/fpga/Kconfig
> >> >> > @@ -156,6 +156,12 @@ config FPGA_DFL_FME_MGR
> >> >> > help
> >> >> > Say Y to enable FPGA Manager driver for FPGA Management Engine.
> >> >> >
> >> >> > +config FPGA_DFL_FME_BRIDGE
> >> >> > + tristate "FPGA DFL FME Bridge Driver"
> >> >> > + depends on FPGA_DFL_FME
> >> >> > + help
> >> >> > + Say Y to enable FPGA Bridge driver for FPGA Management Engine.
> >> >> > +
> >> >> > config FPGA_DFL_PCI
> >> >> > tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
> >> >> > depends on PCI && FPGA_DFL
> >> >> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> >> >> > index f82814a..75096e9 100644
> >> >> > --- a/drivers/fpga/Makefile
> >> >> > +++ b/drivers/fpga/Makefile
> >> >> > @@ -32,6 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> >> >> > obj-$(CONFIG_FPGA_DFL) += dfl.o
> >> >> > obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
> >> >> > obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
> >> >> > +obj-$(CONFIG_FPGA_DFL_FME_BRIDGE) += dfl-fme-br.o
> >> >> >
> >> >> > dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
> >> >> >
> >> >> > diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c
> >> >> > new file mode 100644
> >> >> > index 0000000..5c51b08
> >> >> > --- /dev/null
> >> >> > +++ b/drivers/fpga/dfl-fme-br.c
> >> >> > @@ -0,0 +1,114 @@
> >> >> > +// SPDX-License-Identifier: GPL-2.0
> >> >> > +/*
> >> >> > + * FPGA Bridge Driver for FPGA Management Engine (FME)
> >> >> > + *
> >> >> > + * Copyright (C) 2017 Intel Corporation, Inc.
> >> >> > + *
> >> >> > + * Authors:
> >> >> > + * Wu Hao <[email protected]>
> >> >> > + * Joseph Grecco <[email protected]>
> >> >> > + * Enno Luebbers <[email protected]>
> >> >> > + * Tim Whisonant <[email protected]>
> >> >> > + * Ananda Ravuri <[email protected]>
> >> >> > + * Henry Mitchel <[email protected]>
> >> >> > + */
> >> >> > +
> >> >> > +#include <linux/module.h>
> >> >> > +#include <linux/fpga/fpga-bridge.h>
> >> >> > +
> >> >> > +#include "dfl.h"
> >> >> > +#include "dfl-fme-pr.h"
> >> >> > +
> >> >> > +struct fme_br_priv {
> >> >> > + struct dfl_fme_br_pdata *pdata;
> >> >> > + struct dfl_fpga_port_ops *port_ops;
> >> >> > + struct platform_device *port_pdev;
> >> >> > +};
> >> >> > +
> >> >> > +static int fme_bridge_enable_set(struct fpga_bridge *bridge, bool enable)
> >> >> > +{
> >> >> > + struct fme_br_priv *priv = bridge->priv;
> >> >> > + struct platform_device *port_pdev;
> >> >> > + struct dfl_fpga_port_ops *ops;
> >> >> > +
> >> >> > + if (!priv->port_pdev) {
> >> >> > + port_pdev = dfl_fpga_cdev_find_port(priv->pdata->cdev,
> >> >> > + &priv->pdata->port_id,
> >> >> > + dfl_fpga_check_port_id);
> >> >> > + if (!port_pdev)
> >> >> > + return -ENODEV;
> >> >> > +
> >> >> > + priv->port_pdev = port_pdev;
> >> >> > + }
> >> >> > +
> >> >> > + if (priv->port_pdev && !priv->port_ops) {
> >> >> > + ops = dfl_fpga_get_port_ops(priv->port_pdev);
> >> >> > + if (!ops || !ops->enable_set)
> >> >> > + return -ENOENT;
> >> >> > +
> >> >> > + priv->port_ops = ops;
> >> >> > + }
> >> >>
> >> >> This is saving some pointers. Is it possible that the port_pdev or
> >> >> port_ops could go away?
> >> >
> >> > Hi Alan
> >> >
> >> > Thanks for the comments.
> >> >
> >> > The find_port function will get the port device to prevent that.
> >> > You can see put device in remove function. And it's similar for the
> >> > port ops. In dfl_fpga_get_port_ops function, it will prevent unexpected
> >> > port ops removing by try module get.
> >>
> >> OK, good, and I see the find_port function documents that put_device
> >> is needed, so that's good too.
> >>
> >> When we previously discussed this [1] you described a procedure of
> >> hot-unplugging the VF AFU from the VM and turning it back to PF before
> >> doing the FPGA programming. Some comments on that would be helpful,
> >> maybe located with the port_ops functions.
> >>
> >
> > Hi Alan
> >
> > Actually I plan to add those descriptions about virtualization in the
> > documenation/fpga/dfl.txt, together with the patch which enables the
> > virtualization support for Intel FPGA device (e.g Intel PAC card).
>
> Great
>
> > I
> > think dfl.txt is a better place to have that procedure documented.
> > How do you think?
>
> If it is only in the dff.txt, it may remain unclear (in the code) that
> that is an issue that the code is working around.

No problem, I will add more comments in the code too.

>
> >
> >
> >> [1] https://lkml.org/lkml/2018/4/6/180
> >>
> >> >
> >> >>
> >> >> Also, the port ops routines probably should be named
> >> >> dfl_fpga_port_ops_get/find_port/etc
> >> >>
> >> >
> >> > Hm.. as I see there are functions named as get_device(), put_device(), so
> >> > I just name it as ..._get_port_ops and ..._put_port_ops in similar way. :)
> >> > If you think that could be a better name, it's fine for me to change them.
> >>
> >> Yes, I've seen that too. I think keeping the prefix the same helps to
> >> organize the namespace. Plus :) if I'm grepping through the code, I
> >> can be lazy and not have to use a regex to find the port ops
> >> functions.
> >
> > so we good with current naming?
>
> Recently GregKH give us some guidance about using a common prefix [2].

I see, Thanks.

>
> >
> > some places we find it's get_device() and put_device(), but other functions
> > like device_add(), device_del(). actually it's not a big problem I think, :)
> > currently port_ops has a unified style ..._add/del/get/put_port_ops. I am
> > fine with both kind of naming, if you prefer ...port_ops_add/del/get/put
> > style, please let me know, I can change them in the next version.
>
> Since you have struct dfl_fpga_port_ops, you can just add _{action} to
> the name of the struct and you have dfl_fpga_port_ops_add/del/get/pu

Sure, I will change them in the next version.

Hao

>
> Thanks,
> Alan
>
> [2] https://lkml.org/lkml/2018/3/15/709
>
> >
> > Thanks
> > Hao
> >
> >>
> >> Alan
> >>
> >> >
> >> > Thanks
> >> > Hao
> >> >
> >> >> Alan
> >> >>
> >> >> > +
> >> >> > + return priv->port_ops->enable_set(priv->port_pdev, enable);
> >> >> > +}
> >> >> > +
> >> >> > +static const struct fpga_bridge_ops fme_bridge_ops = {
> >> >> > + .enable_set = fme_bridge_enable_set,
> >> >> > +};
> >> >> > +
> >> >> > +static int fme_br_probe(struct platform_device *pdev)
> >> >> > +{
> >> >> > + struct device *dev = &pdev->dev;
> >> >> > + struct fme_br_priv *priv;
> >> >> > + struct fpga_bridge *br;
> >> >> > + int ret;
> >> >> > +
> >> >> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> >> >> > + if (!priv)
> >> >> > + return -ENOMEM;
> >> >> > +
> >> >> > + priv->pdata = dev_get_platdata(dev);
> >> >> > +
> >> >> > + br = fpga_bridge_create(dev, "DFL FPGA FME Bridge",
> >> >> > + &fme_bridge_ops, priv);
> >> >> > + if (!br)
> >> >> > + return -ENOMEM;
> >> >> > +
> >> >> > + platform_set_drvdata(pdev, br);
> >> >> > +
> >> >> > + ret = fpga_bridge_register(br);
> >> >> > + if (ret)
> >> >> > + fpga_bridge_free(br);
> >> >> > +
> >> >> > + return ret;
> >> >> > +}
> >> >> > +
> >> >> > +static int fme_br_remove(struct platform_device *pdev)
> >> >> > +{
> >> >> > + struct fpga_bridge *br = platform_get_drvdata(pdev);
> >> >> > + struct fme_br_priv *priv = br->priv;
> >> >> > +
> >> >> > + fpga_bridge_unregister(br);
> >> >> > +
> >> >> > + if (priv->port_pdev)
> >> >> > + put_device(&priv->port_pdev->dev);
> >> >> > + if (priv->port_ops)
> >> >> > + dfl_fpga_put_port_ops(priv->port_ops);
> >> >> > +
> >> >> > + return 0;
> >> >> > +}
> >> >> > +
> >> >> > +static struct platform_driver fme_br_driver = {
> >> >> > + .driver = {
> >> >> > + .name = DFL_FPGA_FME_BRIDGE,
> >> >> > + },
> >> >> > + .probe = fme_br_probe,
> >> >> > + .remove = fme_br_remove,
> >> >> > +};
> >> >> > +
> >> >> > +module_platform_driver(fme_br_driver);
> >> >> > +
> >> >> > +MODULE_DESCRIPTION("FPGA Bridge for DFL FPGA Management Engine");
> >> >> > +MODULE_AUTHOR("Intel Corporation");
> >> >> > +MODULE_LICENSE("GPL v2");
> >> >> > +MODULE_ALIAS("platform:dfl-fme-bridge");
> >> >> > --
> >> >> > 1.8.3.1
> >> >> >
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
> >> the body of a message to [email protected]
> >> More majordomo info at http://vger.kernel.org/majordomo-info.html

2018-06-05 20:22:59

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 06/28] fpga: add device feature list support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

I understand that you are implementing support for something that
already has been defined and already exists. With that, I have some
minor suggestions below. I have some questions below about how new
features are added and suggestions for where some comments could be
added to guide anyone who is adding feature devices or sub-features so
they will do it cleanly in the way that you intend. Also some
suggestions so that when a new feature is added, less places in code
have to be touched.

> 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 dfl_fpga_enum_info data structure. Please
> refer to below description for APIs added for enumeration.
>
> Functions for enumeration information preparation:
> *dfl_fpga_enum_info_alloc
> allocate enumeration information data structure.
>
> *dfl_fpga_enum_info_add_dfl
> add a device feature list to dfl_fpga_enum_info data structure.
>
> *dfl_fpga_enum_info_free
> free dfl_fpga_enum_info data structure and related resources.
>
> Functions for feature device enumeration:
> *dfl_fpga_enumerate_feature_devs
> enumerate feature devices and return container device.
>
> *dfl_fpga_remove_feature_devs
> remove feature devices under given container device.

How about dfl_fpga_feature_devs_enumerate/remove?

>
> Signed-off-by: Tim Whisonant <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Zhang Yi <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
> ---
> 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.
> v4: fix SPDX license issue
> rename files to dfl.[ch], fix typo and add more comments.
> remove static feature_info tables for FME and Port.
> remove check on next_afu link list as only FIU has next_afu ptr.
> remove unused macro in header file.
> add more comments for functions.
> v5: add "dfl_" prefix to functions and data structures.
> remove port related functions from DFL framework.
> use BIT_ULL for 64bit register definition.
> save dfl_fpga_cdev in pdata for feature platform devices.
> rebase due to fpga region api changes.
> ---
> drivers/fpga/Kconfig | 16 ++
> drivers/fpga/Makefile | 3 +
> drivers/fpga/dfl.c | 720 ++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/fpga/dfl.h | 279 +++++++++++++++++++
> 4 files changed, 1018 insertions(+)
> create mode 100644 drivers/fpga/dfl.c
> create mode 100644 drivers/fpga/dfl.h
>
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index f47ef84..01ad31f 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -124,4 +124,20 @@ config OF_FPGA_REGION
> Support for loading FPGA images by applying a Device Tree
> overlay.
>
> +config FPGA_DFL
> + tristate "FPGA Device Feature List (DFL) support"
> + select FPGA_BRIDGE
> + select FPGA_REGION
> + help
> + 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 for FPGA.
> + Driver can walk through the feature headers to enumerate feature
> + devices (e.g FPGA Management Engine, Port and Accelerator
> + Function Unit) and their private features for target FPGA devices.
> +
> + Select this option to enable common support for Field-Programmable
> + Gate Array (FPGA) solutions which implement Device Feature List.
> + It provides enumeration APIs, and feature device infrastructure.
> +
> endif # FPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 3cb276a..c4c62b9 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -27,3 +27,6 @@ obj-$(CONFIG_XILINX_PR_DECOUPLER) += xilinx-pr-decoupler.o
> # High Level Interfaces
> obj-$(CONFIG_FPGA_REGION) += fpga-region.o
> obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> +
> +# FPGA Device Feature List Support
> +obj-$(CONFIG_FPGA_DFL) += dfl.o
> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> new file mode 100644
> index 0000000..c1462e9
> --- /dev/null
> +++ b/drivers/fpga/dfl.c
> @@ -0,0 +1,720 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for FPGA Device Feature List (DFL) Support
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.

2017-2018

> + *
> + * Authors:
> + * Kang Luwei <[email protected]>
> + * Zhang Yi <[email protected]>
> + * Wu Hao <[email protected]>
> + * Xiao Guangrong <[email protected]>
> + */
> +#include <linux/module.h>
> +
> +#include "dfl.h"
> +
> +static DEFINE_MUTEX(dfl_id_mutex);
> +
> +enum dfl_id_type {
> + FME_ID, /* fme id allocation and mapping */
> + PORT_ID, /* port id allocation and mapping */
> + DFL_ID_MAX,
> +};

Please add a comment that new values of DFL_ID need to be added to this enum.

It may make sense to add dfl_chrdevs[] (from patch 7) here and add the
DFH_ID id to that struct. That would give you one struct that has all
that info together in one place and new feature drivers would be added
to it. Any translations between dfl_id, char dev name, and
dfl_fpga_devt_type could use that one table, and it could cut down on
the number of if statements and special cases involved in parsing. I
give a few examples of usage below.

> +
> +/* it is protected by dfl_id_mutex */
> +static struct idr dfl_ids[DFL_ID_MAX];
> +
> +static void dfl_ids_init(void)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dfl_ids); i++)
> + idr_init(dfl_ids + i);
> +}
> +
> +static void dfl_ids_destroy(void)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(dfl_ids); i++)
> + idr_destroy(dfl_ids + i);
> +}
> +
> +static int alloc_dfl_id(enum dfl_id_type type, struct device *dev)
> +{
> + int id;
> +
> + WARN_ON(type >= DFL_ID_MAX);
> + mutex_lock(&dfl_id_mutex);
> + id = idr_alloc(dfl_ids + type, dev, 0, 0, GFP_KERNEL);
> + mutex_unlock(&dfl_id_mutex);
> +
> + return id;
> +}
> +
> +static void free_dfl_id(enum dfl_id_type type, int id)
> +{
> + WARN_ON(type >= DFL_ID_MAX);
> + mutex_lock(&dfl_id_mutex);
> + idr_remove(dfl_ids + type, id);
> + mutex_unlock(&dfl_id_mutex);
> +}

We discussed function name groups having a matching prefix on another
thread for another patch. Same thing here, please, such as
dfl_id_alloc/free.

> +
> +static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
> +{
> + if (!strcmp(pdev->name, DFL_FPGA_FEATURE_DEV_FME))
> + return FME_ID;
> +
> + if (!strcmp(pdev->name, DFL_FPGA_FEATURE_DEV_PORT))
> + return PORT_ID;

Will a new if statement need to be added to this function for each
added DFH_ID? IIUC, at the point this function is called, the feature
device exists and has private data. Perhaps it is worth saving the ID
in its private data so you don't have to do this reverse lookup (and
won't have to change this function for each new feature).

Alternatively, if dfl_chrdevs[] is added in this patch, you could use
it here, using a loop that goes through and does the lookup from that
struct and you won't ever have to touch this function again.

In either case, if you add the names of your devices to a table like
dfl_chrdevs[], hopefully things can be coded such that words like
DFL_FPGA_FEATURE_DEV_FME/PORT only have to show up in that table (and
in the individual drivers).

> +
> + WARN_ON(1);
> +
> + return DFL_ID_MAX;
> +}
> +
> +/**
> + * struct 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.
> + * @ioaddr: header register region address of feature device in enumeration.
> + * @sub_features: a sub features link list for feature device in enumeration.
> + * @feature_num: number of sub features for feature device in enumeration.
> + */
> +struct build_feature_devs_info {
> + struct device *dev;
> + struct dfl_fpga_cdev *cdev;
> + struct platform_device *feature_dev;
> + void __iomem *ioaddr;
> + struct list_head sub_features;
> + int feature_num;
> +};
> +
> +/**
> + * struct dfl_feature_info - sub feature info collected during feature dev build
> + *
> + * @fid: id of this sub feature.
> + * @mmio_res: mmio resource of this sub feature.
> + * @ioaddr: mapped base address of mmio resource.
> + * @node: node in sub_features link list.
> + */
> +struct dfl_feature_info {
> + u64 fid;
> + struct resource mmio_res;
> + void __iomem *ioaddr;
> + struct list_head node;
> +};
> +
> +static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev,
> + struct platform_device *port)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&port->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)
> +{
> + struct platform_device *fdev = binfo->feature_dev;
> + struct dfl_feature_platform_data *pdata;
> + struct dfl_feature_info *finfo, *p;
> + int ret, index = 0;
> +
> + if (!fdev)
> + return 0;
> +
> + /*
> + * 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(dfl_feature_platform_data_size(binfo->feature_num),
> + GFP_KERNEL);
> + if (pdata) {
> + pdata->dev = fdev;
> + pdata->num = binfo->feature_num;
> + pdata->dfl_cdev = binfo->cdev;
> + mutex_init(&pdata->lock);
> + } else {
> + return -ENOMEM;
> + }

if (!pdata)
return -ENOMEM;

pdata->dev = fdev; so on

> +
> + /*
> + * 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;
> +
> + /* each sub feature has one MMIO resource */
> + fdev->num_resources = binfo->feature_num;
> + fdev->resource = kcalloc(binfo->feature_num, sizeof(*fdev->resource),
> + GFP_KERNEL);
> + if (!fdev->resource)
> + return -ENOMEM;
> +
> + /* fill features and resource information for feature dev */
> + list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
> + struct dfl_feature *feature = &pdata->features[index];
> +
> + /* save resource information for each feature */
> + feature->id = finfo->fid;
> + feature->resource_index = index;
> + feature->ioaddr = finfo->ioaddr;
> + fdev->resource[index++] = finfo->mmio_res;
> +
> + list_del(&finfo->node);
> + kfree(finfo);
> + }
> +
> + ret = platform_device_add(binfo->feature_dev);
> + if (!ret) {
> + if (feature_dev_id_type(binfo->feature_dev) == PORT_ID)
> + dfl_fpga_cdev_add_port_dev(binfo->cdev,
> + binfo->feature_dev);
> + else
> + 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 dfl_id_type type, const char *name,

If dfl_chrdevs[] were moved to this patch, build_info_create_dev could
use it to look up the name. That way you won't have to have separate
functions for parse_feature_fme and parse_feature_port.

> + void __iomem *ioaddr)
> +{
> + struct platform_device *fdev;
> + 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;
> + binfo->feature_num = 0;
> + binfo->ioaddr = ioaddr;
> + INIT_LIST_HEAD(&binfo->sub_features);
> +
> + fdev->id = alloc_dfl_id(type, &fdev->dev);
> + if (fdev->id < 0)
> + return fdev->id;
> +
> + fdev->dev.parent = &binfo->cdev->region->dev;
> +
> + return 0;
> +}
> +
> +static void build_info_free(struct build_feature_devs_info *binfo)
> +{
> + struct dfl_feature_info *finfo, *p;
> +
> + /*
> + * it is a valid id, free it. See comments in
> + * build_info_create_dev()
> + */
> + if (binfo->feature_dev && binfo->feature_dev->id >= 0) {
> + free_dfl_id(feature_dev_id_type(binfo->feature_dev),
> + binfo->feature_dev->id);
> +
> + list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
> + list_del(&finfo->node);
> + kfree(finfo);
> + }
> + }
> +
> + platform_device_put(binfo->feature_dev);
> +
> + devm_kfree(binfo->dev, binfo);
> +}
> +
> +static inline u32 feature_size(void __iomem *start)
> +{
> + u64 v = readq(start + DFH);
> + u32 ofst = FIELD_GET(DFH_NEXT_HDR_OFST, v);
> + /* workaround for private features with invalid size, use 4K instead */
> + return ofst ? ofst : 4096;
> +}
> +
> +static u64 feature_id(void __iomem *start)
> +{
> + u64 v = readq(start + DFH);
> + u16 id = FIELD_GET(DFH_ID, v);
> + u8 type = FIELD_GET(DFH_TYPE, v);
> +
> + if (type == DFH_TYPE_FIU)
> + return FEATURE_ID_FIU_HEADER;
> + else if (type == DFH_TYPE_PRIVATE)
> + return id;
> + else if (type == DFH_TYPE_AFU)
> + return FEATURE_ID_AFU;
> +
> + WARN_ON(1);
> + return 0;
> +}
> +
> +/*
> + * when create sub feature instances, for private features, it doesn't need
> + * to provide resource size and feature id as they could be read from DFH
> + * register. For afu sub feature, its register region only contains user
> + * defined registers, so never trust any information from it, just use the
> + * resource size information provided by its parent FIU.
> + */
> +static int
> +create_feature_instance(struct build_feature_devs_info *binfo,
> + struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst,
> + resource_size_t size, u64 fid)
> +{
> + struct dfl_feature_info *finfo;
> +
> + /* read feature size and id if inputs are invalid */
> + size = size ? size : feature_size(dfl->ioaddr + ofst);
> + fid = fid ? fid : feature_id(dfl->ioaddr + ofst);
> +
> + if (dfl->len - ofst < size)
> + return -EINVAL;
> +
> + finfo = kzalloc(sizeof(*finfo), GFP_KERNEL);
> + if (!finfo)
> + return -ENOMEM;
> +
> + finfo->fid = fid;
> + finfo->mmio_res.start = dfl->start + ofst;
> + finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
> + finfo->mmio_res.flags = IORESOURCE_MEM;
> + finfo->ioaddr = dfl->ioaddr + ofst;
> +
> + list_add_tail(&finfo->node, &binfo->sub_features);
> + binfo->feature_num++;
> +
> + return 0;
> +}
> +
> +static int parse_feature_fme(struct build_feature_devs_info *binfo,
> + struct dfl_fpga_enum_dfl *dfl,
> + resource_size_t ofst)
> +{
> + int ret;
> +
> + ret = build_info_create_dev(binfo, FME_ID, DFL_FPGA_FEATURE_DEV_FME,
> + dfl->ioaddr + ofst);
> + if (ret)
> + return ret;
> +
> + return create_feature_instance(binfo, dfl, ofst, 0, 0);
> +}
> +
> +static int parse_feature_port(struct build_feature_devs_info *binfo,
> + struct dfl_fpga_enum_dfl *dfl,
> + resource_size_t ofst)
> +{
> + int ret;
> +
> + ret = build_info_create_dev(binfo, PORT_ID, DFL_FPGA_FEATURE_DEV_PORT,
> + dfl->ioaddr + ofst);
> + if (ret)
> + return ret;
> +
> + return create_feature_instance(binfo, dfl, ofst, 0, 0);
> +}
> +
> +static int parse_feature_port_afu(struct build_feature_devs_info *binfo,
> + struct dfl_fpga_enum_dfl *dfl,
> + resource_size_t ofst)
> +{
> + u64 v = readq(binfo->ioaddr + PORT_HDR_CAP);
> + u32 size = FIELD_GET(PORT_CAP_MMIO_SIZE, v) << 10;
> +
> + WARN_ON(!size);
> +
> + return create_feature_instance(binfo, dfl, ofst, size, FEATURE_ID_AFU);
> +}
> +
> +static int parse_feature_afu(struct build_feature_devs_info *binfo,
> + struct dfl_fpga_enum_dfl *dfl,
> + resource_size_t ofst)
> +{
> + if (!binfo->feature_dev) {
> + dev_err(binfo->dev, "this AFU does not belong to any FIU.\n");
> + return -EINVAL;
> + }
> +
> + switch (feature_dev_id_type(binfo->feature_dev)) {
> + case PORT_ID:
> + return parse_feature_port_afu(binfo, dfl, ofst);
> + default:
> + dev_info(binfo->dev, "AFU belonging to FIU %s is not supported yet.\n",
> + binfo->feature_dev->name);
> + }
> +
> + return 0;
> +}
> +
> +static int parse_feature_fiu(struct build_feature_devs_info *binfo,
> + struct dfl_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:
> + ret = parse_feature_fme(binfo, dfl, ofst);
> + break;
> + case DFH_ID_FIU_PORT:
> + ret = parse_feature_port(binfo, dfl, ofst);
> + break;
> + default:
> + dev_info(binfo->dev, "FIU TYPE %d is not supported yet.\n",
> + id);
> + }

If the name lookup is added to build_info_create_dev(), then this
switch statement goes away and the contents of parse_feature_fme/port
are identical and trivial enough to be included here. My reason for
looking for these things is to reduce, where possible, the places
where a function needs to be added or changed to parse each new ID.

> +
> + if (ret)
> + return ret;
> +
> + /* Find and parse FIU's child AFU via its NEXT_AFU register */
> + v = readq(dfl->ioaddr + ofst + NEXT_AFU);
> +
> + offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v);
> + if (offset)
> + return parse_feature_afu(binfo, dfl, ofst + offset);
> +
> + dev_dbg(binfo->dev, "No AFUs detected on FIU %d\n", id);
> +
> + return ret;
> +}
> +
> +static int parse_feature_private(struct build_feature_devs_info *binfo,
> + struct dfl_fpga_enum_dfl *dfl,
> + resource_size_t ofst)
> +{
> + if (!binfo->feature_dev) {
> + dev_err(binfo->dev, "the private feature %llx does not belong to any AFU.\n",
> + (unsigned long long)feature_id(dfl->ioaddr + ofst));
> + return -EINVAL;
> + }
> +
> + return create_feature_instance(binfo, dfl, ofst, 0, 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 dfl_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_afu(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 dfl_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 dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev)
> +{
> + struct dfl_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(dfl_fpga_enum_info_alloc);
> +
> +void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info)
> +{
> + struct dfl_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(dfl_fpga_enum_info_free);
> +
> +/**
> + * dfl_fpga_enum_info_add_dfl - add info of a device feature list to enum info
> + *
> + * @info: ptr to dfl_fpga_enum_info
> + * @start: mmio resource address of the device feature list.
> + * @len: mmio resource length of the device feature list.
> + * @ioaddr: mapped mmio resource address of the device feature list.
> + *
> + * One FPGA device may have 1 or more Device Feature Lists (DFLs), use this
> + * function to add information of each DFL to common data structure for next
> + * step enumeration.
> + *
> + * Return: 0 on success, negative error code otherwise.
> + */
> +int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
> + resource_size_t start, resource_size_t len,
> + void __iomem *ioaddr)
> +{
> + struct dfl_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(dfl_fpga_enum_info_add_dfl);
> +
> +static int remove_feature_dev(struct device *dev, void *data)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + enum dfl_id_type type = feature_dev_id_type(pdev);
> + int id = pdev->id;
> +
> + platform_device_unregister(pdev);
> +
> + free_dfl_id(type, id);
> +
> + return 0;
> +}
> +
> +static void remove_feature_devs(struct dfl_fpga_cdev *cdev)
> +{
> + device_for_each_child(&cdev->region->dev, NULL, remove_feature_dev);
> +}
> +
> +/**
> + * dfl_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: dfl_fpga_cdev struct on success, -errno on failure
> + */
> +struct dfl_fpga_cdev *
> +dfl_fpga_enumerate_feature_devs(struct dfl_fpga_enum_info *info)
> +{
> + struct build_feature_devs_info *binfo;
> + struct dfl_fpga_enum_dfl *dfl;
> + struct dfl_fpga_cdev *cdev;
> + 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->region = fpga_region_create(info->dev, NULL, NULL);
> + if (!cdev->region) {
> + ret = -ENOMEM;
> + goto free_region_exit;
> + }
> +
> + cdev->parent = info->dev;
> + mutex_init(&cdev->lock);
> + INIT_LIST_HEAD(&cdev->port_dev_list);
> +
> + 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_region_exit:
> + fpga_region_free(cdev->region);
> +free_cdev_exit:
> + devm_kfree(info->dev, cdev);
> + return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(dfl_fpga_enumerate_feature_devs);
> +
> +/**
> + * dfl_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 dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev)
> +{
> + struct dfl_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(dfl_fpga_remove_feature_devs);
> +
> +static int __init dfl_fpga_init(void)
> +{
> + dfl_ids_init();
> +
> + return 0;
> +}
> +
> +static void __exit dfl_fpga_exit(void)
> +{
> + dfl_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");
> diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
> new file mode 100644
> index 0000000..2ede915
> --- /dev/null
> +++ b/drivers/fpga/dfl.h
> @@ -0,0 +1,279 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Driver Header File for FPGA Device Feature List (DFL) Support
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + * Kang Luwei <[email protected]>
> + * Zhang Yi <[email protected]>
> + * Wu Hao <[email protected]>
> + * Xiao Guangrong <[email protected]>
> + */
> +
> +#ifndef __FPGA_DFL_H
> +#define __FPGA_DFL_H
> +
> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/iopoll.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/uuid.h>
> +#include <linux/fpga/fpga-region.h>
> +
> +/* maximum supported number of ports */
> +#define MAX_DFL_FPGA_PORT_NUM 4
> +/* plus one for fme device */
> +#define MAX_DFL_FEATURE_DEV_NUM (MAX_DFL_FPGA_PORT_NUM + 1)
> +
> +/* Reserved 0x0 for Header Group Register and 0xff for AFU */
> +#define FEATURE_ID_FIU_HEADER 0x0
> +#define FEATURE_ID_AFU 0xff
> +
> +#define FME_FEATURE_ID_HEADER FEATURE_ID_FIU_HEADER
> +#define FME_FEATURE_ID_THERMAL_MGMT 0x1
> +#define FME_FEATURE_ID_POWER_MGMT 0x2
> +#define FME_FEATURE_ID_GLOBAL_IPERF 0x3
> +#define FME_FEATURE_ID_GLOBAL_ERR 0x4
> +#define FME_FEATURE_ID_PR_MGMT 0x5
> +#define FME_FEATURE_ID_HSSI 0x6
> +#define FME_FEATURE_ID_GLOBAL_DPERF 0x7
> +
> +#define PORT_FEATURE_ID_HEADER FEATURE_ID_FIU_HEADER
> +#define PORT_FEATURE_ID_AFU FEATURE_ID_AFU
> +#define PORT_FEATURE_ID_ERROR 0x10
> +#define PORT_FEATURE_ID_UMSG 0x11
> +#define PORT_FEATURE_ID_UINT 0x12
> +#define PORT_FEATURE_ID_STP 0x13
> +
> +/*
> + * Device Feature Header Register Set
> + *
> + * For FIUs, they all have DFH + GUID + NEXT_AFU as common header registers.
> + * For AFUs, they have DFH + GUID as common header registers.
> + * For private features, they only have DFH register as common header.
> + */
> +#define DFH 0x0
> +#define GUID_L 0x8
> +#define GUID_H 0x10
> +#define NEXT_AFU 0x18
> +
> +#define DFH_SIZE 0x8
> +
> +/* Device Feature Header Register Bitfield */
> +#define DFH_ID GENMASK_ULL(11, 0) /* Feature ID */
> +#define DFH_ID_FIU_FME 0
> +#define DFH_ID_FIU_PORT 1
> +#define DFH_REVISION GENMASK_ULL(15, 12) /* Feature revision */
> +#define DFH_NEXT_HDR_OFST GENMASK_ULL(39, 16) /* Offset to next DFH */
> +#define DFH_EOL BIT_ULL(40) /* End of list */
> +#define DFH_TYPE GENMASK_ULL(63, 60) /* Feature type */
> +#define DFH_TYPE_AFU 1
> +#define DFH_TYPE_PRIVATE 3
> +#define DFH_TYPE_FIU 4
> +
> +/* Next AFU Register Bitfield */
> +#define NEXT_AFU_NEXT_DFH_OFST GENMASK_ULL(23, 0) /* Offset to next AFU */
> +
> +/* FME Header Register Set */
> +#define FME_HDR_DFH DFH
> +#define FME_HDR_GUID_L GUID_L
> +#define FME_HDR_GUID_H GUID_H
> +#define FME_HDR_NEXT_AFU NEXT_AFU
> +#define FME_HDR_CAP 0x30
> +#define FME_HDR_PORT_OFST(n) (0x38 + ((n) * 0x8))
> +#define FME_HDR_BITSTREAM_ID 0x60
> +#define FME_HDR_BITSTREAM_MD 0x68
> +
> +/* FME Fab Capability Register Bitfield */
> +#define FME_CAP_FABRIC_VERID GENMASK_ULL(7, 0) /* Fabric version ID */
> +#define FME_CAP_SOCKET_ID BIT_ULL(8) /* Socket ID */
> +#define FME_CAP_PCIE0_LINK_AVL BIT_ULL(12) /* PCIE0 Link */
> +#define FME_CAP_PCIE1_LINK_AVL BIT_ULL(13) /* PCIE1 Link */
> +#define FME_CAP_COHR_LINK_AVL BIT_ULL(14) /* Coherent Link */
> +#define FME_CAP_IOMMU_AVL BIT_ULL(16) /* IOMMU available */
> +#define FME_CAP_NUM_PORTS GENMASK_ULL(19, 17) /* Number of ports */
> +#define FME_CAP_ADDR_WIDTH GENMASK_ULL(29, 24) /* Address bus width */
> +#define FME_CAP_CACHE_SIZE GENMASK_ULL(43, 32) /* cache size in KB */
> +#define FME_CAP_CACHE_ASSOC GENMASK_ULL(47, 44) /* Associativity */
> +
> +/* FME Port Offset Register Bitfield */
> +/* Offset to port device feature header */
> +#define FME_PORT_OFST_DFH_OFST GENMASK_ULL(23, 0)
> +/* PCI Bar ID for this port */
> +#define FME_PORT_OFST_BAR_ID GENMASK_ULL(34, 32)
> +/* AFU MMIO access permission. 1 - VF, 0 - PF. */
> +#define FME_PORT_OFST_ACC_CTRL BIT_ULL(55)
> +#define FME_PORT_OFST_ACC_PF 0
> +#define FME_PORT_OFST_ACC_VF 1
> +#define FME_PORT_OFST_IMP BIT_ULL(60)
> +
> +/* PORT Header Register Set */
> +#define PORT_HDR_DFH DFH
> +#define PORT_HDR_GUID_L GUID_L
> +#define PORT_HDR_GUID_H GUID_H
> +#define PORT_HDR_NEXT_AFU NEXT_AFU
> +#define PORT_HDR_CAP 0x30
> +#define PORT_HDR_CTRL 0x38
> +
> +/* Port Capability Register Bitfield */
> +#define PORT_CAP_PORT_NUM GENMASK_ULL(1, 0) /* ID of this port */
> +#define PORT_CAP_MMIO_SIZE GENMASK_ULL(23, 8) /* MMIO size in KB */
> +#define PORT_CAP_SUPP_INT_NUM GENMASK_ULL(35, 32) /* Interrupts num */
> +
> +/* Port Control Register Bitfield */
> +#define PORT_CTRL_SFTRST BIT_ULL(0) /* Port soft reset */
> +/* Latency tolerance reporting. '1' >= 40us, '0' < 40us.*/
> +#define PORT_CTRL_LATENCY BIT_ULL(2)
> +#define PORT_CTRL_SFTRST_ACK BIT_ULL(4) /* HW ack for reset */
> +
> +/**
> + * struct dfl_feature - sub feature of the feature devices
> + *
> + * @id: sub feature id.
> + * @resource_index: each sub feature has one mmio resource for its registers.
> + * this index is used to find its mmio resource from the
> + * feature dev (platform device)'s reources.
> + * @ioaddr: mapped mmio resource address.
> + */
> +struct dfl_feature {
> + u64 id;
> + int resource_index;
> + void __iomem *ioaddr;
> +};
> +
> +/**
> + * struct dfl_feature_platform_data - platform data for feature devices
> + *
> + * @node: node to link feature devs to container device's port_dev_list.
> + * @lock: mutex to protect platform data.
> + * @dev: ptr to platform device linked with this platform data.
> + * @dfl_cdev: ptr to container device.
> + * @disable_count: count for port disable.
> + * @num: number for sub features.
> + * @features: sub features of this feature dev.
> + */
> +struct dfl_feature_platform_data {
> + struct list_head node;
> + struct mutex lock;
> + struct platform_device *dev;
> + struct dfl_fpga_cdev *dfl_cdev;
> + unsigned int disable_count;
> +
> + int num;
> + struct dfl_feature features[0];
> +};
> +
> +#define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
> +#define DFL_FPGA_FEATURE_DEV_PORT "dfl-port"
> +
> +static inline int dfl_feature_platform_data_size(const int num)
> +{
> + return sizeof(struct dfl_feature_platform_data) +
> + num * sizeof(struct dfl_feature);
> +}
> +
> +#define dfl_fpga_dev_for_each_feature(pdata, feature) \
> + for ((feature) = (pdata)->features; \
> + (feature) < (pdata)->features + (pdata)->num; (feature)++)
> +
> +static inline
> +struct dfl_feature *dfl_get_feature_by_id(struct device *dev, u64 id)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
> + struct dfl_feature *feature;
> +
> + dfl_fpga_dev_for_each_feature(pdata, feature)
> + if (feature->id == id)
> + return feature;
> +
> + return NULL;
> +}
> +
> +static inline
> +void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u64 id)
> +{
> + struct dfl_feature *feature = dfl_get_feature_by_id(dev, id);
> +
> + if (feature && feature->ioaddr)
> + return feature->ioaddr;
> +
> + WARN_ON(1);
> + return NULL;
> +}
> +
> +static inline bool dfl_feature_is_fme(void __iomem *base)
> +{
> + u64 v = readq(base + DFH);
> +
> + return (FIELD_GET(DFH_TYPE, v) == DFH_TYPE_FIU) &&
> + (FIELD_GET(DFH_ID, v) == DFH_ID_FIU_FME);
> +}
> +
> +static inline bool dfl_feature_is_port(void __iomem *base)
> +{
> + u64 v = readq(base + DFH);
> +
> + return (FIELD_GET(DFH_TYPE, v) == DFH_TYPE_FIU) &&
> + (FIELD_GET(DFH_ID, v) == DFH_ID_FIU_PORT);
> +}
> +
> +/**
> + * struct dfl_fpga_enum_info - DFL FPGA enumeration information
> + *
> + * @dev: parent device.
> + * @dfls: list of device feature lists.
> + */
> +struct dfl_fpga_enum_info {
> + struct device *dev;
> + struct list_head dfls;
> +};
> +
> +/**
> + * struct dfl_fpga_enum_dfl - DFL FPGA enumeration device feature list info
> + *
> + * @start: base address of this device feature list.
> + * @len: size of this device feature list.
> + * @ioaddr: mapped base address of this device feature list.
> + * @node: node in list of device feature lists.
> + */
> +struct dfl_fpga_enum_dfl {
> + resource_size_t start;
> + resource_size_t len;
> +
> + void __iomem *ioaddr;
> +
> + struct list_head node;
> +};
> +
> +struct dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev);
> +int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
> + resource_size_t start, resource_size_t len,
> + void __iomem *ioaddr);
> +void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info);
> +
> +/**
> + * struct dfl_fpga_cdev - container device of DFL based FPGA
> + *
> + * @parent: parent device of this container device.
> + * @region: base fpga region.
> + * @fme_dev: FME feature device under this container device.
> + * @lock: mutex lock to protect the port device list.
> + * @port_dev_list: list of all port feature devices under this container device.
> + */
> +struct dfl_fpga_cdev {
> + struct device *parent;
> + struct fpga_region *region;
> + struct device *fme_dev;
> + struct mutex lock;
> + struct list_head port_dev_list;
> +};
> +
> +struct dfl_fpga_cdev *
> +dfl_fpga_enumerate_feature_devs(struct dfl_fpga_enum_info *info);
> +void dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev);
> +
> +#endif /* __FPGA_DFL_H */
> --
> 1.8.3.1
>

2018-06-05 20:23:24

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 07/28] fpga: dfl: add chardev support for feature devices

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> 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 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 <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Zhang Yi <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
> ---
> v2: rebased
> v3: move chardev support to fpga-dfl framework
> v4: rebase, and add more comments in code.
> v5: rebase, and add dfl_ prefix to APIs and data structures.
> ---
> drivers/fpga/dfl.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
> drivers/fpga/dfl.h | 14 ++++++++
> 2 files changed, 117 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> index c1462e9..18aba02 100644
> --- a/drivers/fpga/dfl.c
> +++ b/drivers/fpga/dfl.c
> @@ -74,6 +74,96 @@ static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
> return DFL_ID_MAX;
> }
>
> +struct dfl_chardev_info {
> + const char *name;
> + dev_t devt;
> +};
> +
> +/* indexed by enum dfl_fpga_devt_type */
> +struct dfl_chardev_info dfl_chrdevs[] = {
> + {.name = DFL_FPGA_FEATURE_DEV_FME}, /* DFL_FPGA_DEVT_FME */
> + {.name = DFL_FPGA_FEATURE_DEV_PORT}, /* DFL_FPGA_DEVT_AFU */
> +};

If this were added in the initial dfl.c patch, it could be used by
build_info_create_dev to get the name.

> +
> +static void dfl_chardev_uinit(void)
> +{
> + int i;
> +
> + for (i = 0; i < DFL_FPGA_DEVT_MAX; i++)
> + if (MAJOR(dfl_chrdevs[i].devt)) {
> + unregister_chrdev_region(dfl_chrdevs[i].devt,
> + MINORMASK);
> + dfl_chrdevs[i].devt = MKDEV(0, 0);
> + }
> +}
> +
> +static int dfl_chardev_init(void)
> +{
> + int i, ret;
> +
> + for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) {
> + ret = alloc_chrdev_region(&dfl_chrdevs[i].devt, 0, MINORMASK,
> + dfl_chrdevs[i].name);
> + if (ret)
> + goto exit;
> + }
> +
> + return 0;
> +
> +exit:
> + dfl_chardev_uinit();
> + return ret;
> +}
> +
> +static dev_t dfl_get_devt(enum dfl_fpga_devt_type type, int id)
> +{
> + WARN_ON(type >= DFL_FPGA_DEVT_MAX);
> +
> + return MKDEV(MAJOR(dfl_chrdevs[type].devt), id);
> +}
> +
> +/**
> + * dfl_fpga_register_dev_ops - register cdev ops for feature dev
> + *
> + * @pdev: feature dev.
> + * @fops: file operations for feature dev's cdev.
> + * @owner: owning module/driver.
> + *
> + * Return: 0 on success, negative error code otherwise.
> + */
> +int dfl_fpga_register_dev_ops(struct platform_device *pdev,
> + const struct file_operations *fops,
> + struct module *owner)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +
> + cdev_init(&pdata->cdev, fops);
> + pdata->cdev.owner = owner;
> +
> + /*
> + * set parent to the feature device so that its refcount is
> + * decreased after the last refcount of cdev is gone, that
> + * makes sure the feature device is valid during device
> + * file's life-cycle.
> + */
> + pdata->cdev.kobj.parent = &pdev->dev.kobj;
> +
> + return cdev_add(&pdata->cdev, pdev->dev.devt, 1);
> +}
> +EXPORT_SYMBOL_GPL(dfl_fpga_register_dev_ops);
> +
> +/**
> + * dfl_fpga_unregister_dev_ops - unregister cdev ops for feature dev
> + * @pdev: feature dev.
> + */
> +void dfl_fpga_unregister_dev_ops(struct platform_device *pdev)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +
> + cdev_del(&pdata->cdev);
> +}
> +EXPORT_SYMBOL_GPL(dfl_fpga_unregister_dev_ops);
> +
> /**
> * struct build_feature_devs_info - info collected during feature dev build.
> *
> @@ -208,9 +298,13 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
> enum dfl_id_type type, const char *name,
> void __iomem *ioaddr)
> {
> + enum dfl_fpga_devt_type devt_type = DFL_FPGA_DEVT_FME;
> struct platform_device *fdev;
> int ret;
>
> + if (type == PORT_ID)
> + devt_type = DFL_FPGA_DEVT_PORT;
> +
> /* we will create a new device, commit current device first */
> ret = build_info_commit_dev(binfo);
> if (ret)
> @@ -234,6 +328,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
> return fdev->id;
>
> fdev->dev.parent = &binfo->cdev->region->dev;
> + fdev->dev.devt = dfl_get_devt(devt_type, fdev->id);
>
> return 0;
> }
> @@ -702,13 +797,20 @@ void dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev)
>
> static int __init dfl_fpga_init(void)
> {
> + int ret;
> +
> dfl_ids_init();
>
> - return 0;
> + ret = dfl_chardev_init();
> + if (ret)
> + dfl_ids_destroy();
> +
> + return ret;
> }
>
> static void __exit dfl_fpga_exit(void)
> {
> + dfl_chardev_uinit();
> dfl_ids_destroy();
> }
>
> diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
> index 2ede915..5fcb1a1 100644
> --- a/drivers/fpga/dfl.h
> +++ b/drivers/fpga/dfl.h
> @@ -15,6 +15,7 @@
> #define __FPGA_DFL_H
>
> #include <linux/bitfield.h>
> +#include <linux/cdev.h>
> #include <linux/delay.h>
> #include <linux/fs.h>
> #include <linux/iopoll.h>
> @@ -150,6 +151,7 @@ struct dfl_feature {
> *
> * @node: node to link feature devs to container device's port_dev_list.
> * @lock: mutex to protect platform data.
> + * @cdev: cdev of feature dev.
> * @dev: ptr to platform device linked with this platform data.
> * @dfl_cdev: ptr to container device.
> * @disable_count: count for port disable.
> @@ -159,6 +161,7 @@ struct dfl_feature {
> struct dfl_feature_platform_data {
> struct list_head node;
> struct mutex lock;
> + struct cdev cdev;
> struct platform_device *dev;
> struct dfl_fpga_cdev *dfl_cdev;
> unsigned int disable_count;
> @@ -176,6 +179,17 @@ static inline int dfl_feature_platform_data_size(const int num)
> num * sizeof(struct dfl_feature);
> }
>
> +enum dfl_fpga_devt_type {
> + DFL_FPGA_DEVT_FME,
> + DFL_FPGA_DEVT_PORT,
> + DFL_FPGA_DEVT_MAX,
> +};

Could you move this enum to be close to other similar enums (like
dfl_id_type)? The dfl code has a few enums that are similar, and may
need updating (or not) as feature are added. Putting them close
together with appropriate comments would be helpful to keep them all
straight.

> +
> +int dfl_fpga_register_dev_ops(struct platform_device *pdev,
> + const struct file_operations *fops,
> + struct module *owner);
> +void dfl_fpga_unregister_dev_ops(struct platform_device *pdev);
> +
> #define dfl_fpga_dev_for_each_feature(pdata, feature) \
> for ((feature) = (pdata)->features; \
> (feature) < (pdata)->features + (pdata)->num; (feature)++)
> --
> 1.8.3.1
>

2018-06-05 21:16:02

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 09/28] fpga: dfl: add feature device infrastructure

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

Some minor things, otherwise looks fine.

> From: Xiao Guangrong <[email protected]>
>
> 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 dfl_feature_dev_use_begin/end which are used to ensure
> exclusive usage of the feature device file.
>
> Signed-off-by: Tim Whisonant <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Kang Luwei <[email protected]>
> Signed-off-by: Zhang Yi <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> v2: rebased
> v3: use const for feature_ops.
> replace pci related function.
> v4: rebase and add more comments in code.
> v5: remove useless WARN_ON().
> reorder declarations in functions per suggestion from Moritz.
> add "dfl_" prefix to functions and data structure.
> ---
> drivers/fpga/dfl.c | 57 +++++++++++++++++++++++++++++++++++++
> drivers/fpga/dfl.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 138 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> index 1e06efb..c4c47d6 100644
> --- a/drivers/fpga/dfl.c
> +++ b/drivers/fpga/dfl.c
> @@ -74,6 +74,63 @@ static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
> return DFL_ID_MAX;
> }
>
> +void dfl_fpga_dev_feature_uinit(struct platform_device *pdev)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> + struct dfl_feature *feature;
> +
> + dfl_fpga_dev_for_each_feature(pdata, feature)
> + if (feature->ops) {
> + feature->ops->uinit(pdev, feature);
> + feature->ops = NULL;
> + }
> +}
> +EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit);

Please add kernel-doc for functions that are exported to recommend and
guide their usage.

> +
> +static int dfl_feature_instance_init(struct platform_device *pdev,
> + struct dfl_feature_platform_data *pdata,
> + struct dfl_feature *feature,
> + struct dfl_feature_driver *drv)
> +{
> + int ret;
> +
> + ret = drv->ops->init(pdev, feature);
> + if (ret)
> + return ret;
> +
> + feature->ops = drv->ops;
> +
> + return ret;
> +}
> +
> +int dfl_fpga_dev_feature_init(struct platform_device *pdev,
> + struct dfl_feature_driver *feature_drvs)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> + struct dfl_feature_driver *drv = feature_drvs;
> + struct dfl_feature *feature;
> + int ret;
> +
> + while (drv->ops) {
> + dfl_fpga_dev_for_each_feature(pdata, feature) {
> + /* match feature and drv using id */
> + if (feature->id == drv->id) {
> + ret = dfl_feature_instance_init(pdev, pdata,
> + feature, drv);
> + if (ret)
> + goto exit;
> + }
> + }
> + drv++;
> + }
> +
> + return 0;
> +exit:
> + dfl_fpga_dev_feature_uinit(pdev);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_init);
> +
> struct dfl_chardev_info {
> const char *name;
> dev_t devt;
> diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
> index 2b6aaef..27f7a74 100644
> --- a/drivers/fpga/dfl.h
> +++ b/drivers/fpga/dfl.h
> @@ -132,6 +132,17 @@
> #define PORT_CTRL_SFTRST_ACK BIT_ULL(4) /* HW ack for reset */
>
> /**
> + * struct dfl_feature_driver - sub feature's driver
> + *
> + * @id: sub feature id.
> + * @ops: ops of this sub feature.
> + */
> +struct dfl_feature_driver {
> + u64 id;
> + const struct dfl_feature_ops *ops;
> +};
> +
> +/**
> * struct dfl_feature - sub feature of the feature devices
> *
> * @id: sub feature id.
> @@ -139,13 +150,17 @@
> * this index is used to find its mmio resource from the
> * feature dev (platform device)'s reources.
> * @ioaddr: mapped mmio resource address.
> + * @ops: ops of this sub feature.
> */
> struct dfl_feature {
> u64 id;
> int resource_index;
> void __iomem *ioaddr;
> + const struct dfl_feature_ops *ops;
> };
>
> +#define DEV_STATUS_IN_USE 0
> +
> /**
> * struct dfl_feature_platform_data - platform data for feature devices
> *
> @@ -156,6 +171,8 @@ struct dfl_feature {
> * @dfl_cdev: ptr to container device.
> * @disable_count: count for port disable.
> * @num: number for sub features.
> + * @dev_status: dev status (e.g DEV_STATUS_IN_USE).
> + * @private: ptr to feature dev private data.
> * @features: sub features of this feature dev.
> */
> struct dfl_feature_platform_data {
> @@ -165,11 +182,49 @@ struct dfl_feature_platform_data {
> struct platform_device *dev;
> struct dfl_fpga_cdev *dfl_cdev;
> unsigned int disable_count;
> -
> + unsigned long dev_status;
> + void *private;
> int num;
> struct dfl_feature features[0];
> };
>
> +static inline
> +int dfl_feature_dev_use_begin(struct dfl_feature_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;
> +
> + return 0;
> +}
> +
> +static inline
> +void dfl_feature_dev_use_end(struct dfl_feature_platform_data *pdata)
> +{
> + clear_bit_unlock(DEV_STATUS_IN_USE, &pdata->dev_status);
> +}
> +
> +static inline
> +void dfl_fpga_pdata_set_private(struct dfl_feature_platform_data *pdata,
> + void *private)
> +{
> + pdata->private = private;
> +}
> +
> +static inline
> +void *dfl_fpga_pdata_get_private(struct dfl_feature_platform_data *pdata)
> +{
> + return pdata->private;
> +}
> +
> +struct dfl_feature_ops {
> + int (*init)(struct platform_device *pdev, struct dfl_feature *feature);
> + void (*uinit)(struct platform_device *pdev,
> + struct dfl_feature *feature);
> + long (*ioctl)(struct platform_device *pdev, struct dfl_feature *feature,
> + unsigned int cmd, unsigned long arg);
> +};
> +
> #define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
> #define DFL_FPGA_FEATURE_DEV_PORT "dfl-port"

Please move these to the same place as other things that will need to
be added to as feature devices are added as noted in the other reviews
today.

>
> @@ -179,6 +234,10 @@ static inline int dfl_feature_platform_data_size(const int num)
> num * sizeof(struct dfl_feature);
> }
>
> +void dfl_fpga_dev_feature_uinit(struct platform_device *pdev);
> +int dfl_fpga_dev_feature_init(struct platform_device *pdev,
> + struct dfl_feature_driver *feature_drvs);
> +
> enum dfl_fpga_devt_type {
> DFL_FPGA_DEVT_FME,
> DFL_FPGA_DEVT_PORT,
> @@ -190,6 +249,16 @@ int dfl_fpga_register_dev_ops(struct platform_device *pdev,
> struct module *owner);
> void dfl_fpga_unregister_dev_ops(struct platform_device *pdev);
>
> +static inline
> +struct platform_device *dfl_fpga_inode_to_feature_dev(struct inode *inode)
> +{
> + struct dfl_feature_platform_data *pdata;
> +
> + pdata = container_of(inode->i_cdev, struct dfl_feature_platform_data,
> + cdev);
> + return pdata->dev;
> +}
> +
> #define dfl_fpga_dev_for_each_feature(pdata, feature) \
> for ((feature) = (pdata)->features; \
> (feature) < (pdata)->features + (pdata)->num; (feature)++)
> @@ -219,6 +288,17 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u64 id)
> return NULL;
> }
>
> +static inline bool is_dfl_feature_present(struct device *dev, u64 id)
> +{
> + return !!dfl_get_feature_ioaddr_by_id(dev, id);
> +}
> +
> +static inline
> +struct device *dfl_fpga_pdata_to_parent(struct dfl_feature_platform_data *pdata)
> +{
> + return pdata->dev->dev.parent->parent;
> +}
> +
> static inline bool dfl_feature_is_fme(void __iomem *base)
> {
> u64 v = readq(base + DFH);
> --
> 1.8.3.1
>

2018-06-05 21:25:53

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 10/28] fpga: dfl: add dfl_fpga_port_ops support.

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> In some cases, other DFL driver modules may need to access some port
> operations, e.g disable / enable port for partial reconfiguration in
> FME module. In order to avoid dependency between port and FME modules,
> this patch introduces the dfl_fpga_port_ops support in DFL framework.
> A global dfl_fpga_port_ops list is added in the DFL framework, and
> it allows other DFL modules to use these port operations registered
> to this list, even in virtualization case, the port platform device
> is turned into VF / guest VM and hidden in host, the registered
> port_ops is still usable. It resolves the dependency issues between
> modules, but once get port ops API returns a valid port ops, that
> means related port driver module has been module_get to prevent from
> unexpected unload, and put port ops API must be invoked after use.
>
> These APIs introduced by this patch is listed below:
> * dfl_fpga_add_port_ops
> add one port ops to the global list.
>
> * dfl_fpga_del_port_ops
> del one port ops from the global list.
>
> * dfl_fpga_get_port_ops / dfl_fpga_put_port_ops
> get/put the port ops before/after use.
>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>
> ---
> drivers/fpga/dfl.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/fpga/dfl.h | 21 +++++++++++++++++
> 2 files changed, 88 insertions(+)
>

2018-06-05 21:28:04

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 11/28] fpga: dfl: add dfl_fpga_check_port_id function.

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> This patch adds one common function in DFL framework. It uses
> port_ops get_id callback to get port id and compare it with given
> value. This function could be used as match function of the
> dfl_fpga_cdev_find_port function.
>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

2018-06-06 12:33:56

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 06/28] fpga: add device feature list support

On Tue, Jun 05, 2018 at 03:21:31PM -0500, Alan Tull wrote:
> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>
> Hi Hao,
>
> I understand that you are implementing support for something that
> already has been defined and already exists. With that, I have some
> minor suggestions below. I have some questions below about how new
> features are added and suggestions for where some comments could be
> added to guide anyone who is adding feature devices or sub-features so
> they will do it cleanly in the way that you intend. Also some
> suggestions so that when a new feature is added, less places in code
> have to be touched.

Hi Alan,

I fully understand your point, thanks a lot for the review. Please see
my response below.

>
> > 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 dfl_fpga_enum_info data structure. Please
> > refer to below description for APIs added for enumeration.
> >
> > Functions for enumeration information preparation:
> > *dfl_fpga_enum_info_alloc
> > allocate enumeration information data structure.
> >
> > *dfl_fpga_enum_info_add_dfl
> > add a device feature list to dfl_fpga_enum_info data structure.
> >
> > *dfl_fpga_enum_info_free
> > free dfl_fpga_enum_info data structure and related resources.
> >
> > Functions for feature device enumeration:
> > *dfl_fpga_enumerate_feature_devs
> > enumerate feature devices and return container device.
> >
> > *dfl_fpga_remove_feature_devs
> > remove feature devices under given container device.
>
> How about dfl_fpga_feature_devs_enumerate/remove?

Sure, will rename it in v6.

>
> >
> > Signed-off-by: Tim Whisonant <[email protected]>
> > Signed-off-by: Enno Luebbers <[email protected]>
> > Signed-off-by: Shiva Rao <[email protected]>
> > Signed-off-by: Christopher Rauer <[email protected]>
> > Signed-off-by: Zhang Yi <[email protected]>
> > Signed-off-by: Xiao Guangrong <[email protected]>
> > Signed-off-by: Wu Hao <[email protected]>
> > ---
> > 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.
> > v4: fix SPDX license issue
> > rename files to dfl.[ch], fix typo and add more comments.
> > remove static feature_info tables for FME and Port.
> > remove check on next_afu link list as only FIU has next_afu ptr.
> > remove unused macro in header file.
> > add more comments for functions.
> > v5: add "dfl_" prefix to functions and data structures.
> > remove port related functions from DFL framework.
> > use BIT_ULL for 64bit register definition.
> > save dfl_fpga_cdev in pdata for feature platform devices.
> > rebase due to fpga region api changes.
> > ---
> > drivers/fpga/Kconfig | 16 ++
> > drivers/fpga/Makefile | 3 +
> > drivers/fpga/dfl.c | 720 ++++++++++++++++++++++++++++++++++++++++++++++++++
> > drivers/fpga/dfl.h | 279 +++++++++++++++++++
> > 4 files changed, 1018 insertions(+)
> > create mode 100644 drivers/fpga/dfl.c
> > create mode 100644 drivers/fpga/dfl.h
> >
> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> > index f47ef84..01ad31f 100644
> > --- a/drivers/fpga/Kconfig
> > +++ b/drivers/fpga/Kconfig
> > @@ -124,4 +124,20 @@ config OF_FPGA_REGION
> > Support for loading FPGA images by applying a Device Tree
> > overlay.
> >
> > +config FPGA_DFL
> > + tristate "FPGA Device Feature List (DFL) support"
> > + select FPGA_BRIDGE
> > + select FPGA_REGION
> > + help
> > + 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 for FPGA.
> > + Driver can walk through the feature headers to enumerate feature
> > + devices (e.g FPGA Management Engine, Port and Accelerator
> > + Function Unit) and their private features for target FPGA devices.
> > +
> > + Select this option to enable common support for Field-Programmable
> > + Gate Array (FPGA) solutions which implement Device Feature List.
> > + It provides enumeration APIs, and feature device infrastructure.
> > +
> > endif # FPGA
> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> > index 3cb276a..c4c62b9 100644
> > --- a/drivers/fpga/Makefile
> > +++ b/drivers/fpga/Makefile
> > @@ -27,3 +27,6 @@ obj-$(CONFIG_XILINX_PR_DECOUPLER) += xilinx-pr-decoupler.o
> > # High Level Interfaces
> > obj-$(CONFIG_FPGA_REGION) += fpga-region.o
> > obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> > +
> > +# FPGA Device Feature List Support
> > +obj-$(CONFIG_FPGA_DFL) += dfl.o
> > diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> > new file mode 100644
> > index 0000000..c1462e9
> > --- /dev/null
> > +++ b/drivers/fpga/dfl.c
> > @@ -0,0 +1,720 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Driver for FPGA Device Feature List (DFL) Support
> > + *
> > + * Copyright (C) 2017 Intel Corporation, Inc.
>
> 2017-2018

I will fix this in all driver files in v6.

>
> > + *
> > + * Authors:
> > + * Kang Luwei <[email protected]>
> > + * Zhang Yi <[email protected]>
> > + * Wu Hao <[email protected]>
> > + * Xiao Guangrong <[email protected]>
> > + */
> > +#include <linux/module.h>
> > +
> > +#include "dfl.h"
> > +
> > +static DEFINE_MUTEX(dfl_id_mutex);
> > +
> > +enum dfl_id_type {
> > + FME_ID, /* fme id allocation and mapping */
> > + PORT_ID, /* port id allocation and mapping */
> > + DFL_ID_MAX,
> > +};
>
> Please add a comment that new values of DFL_ID need to be added to this enum.
>
> It may make sense to add dfl_chrdevs[] (from patch 7) here and add the
> DFH_ID id to that struct. That would give you one struct that has all
> that info together in one place and new feature drivers would be added
> to it. Any translations between dfl_id, char dev name, and
> dfl_fpga_devt_type could use that one table, and it could cut down on
> the number of if statements and special cases involved in parsing. I
> give a few examples of usage below.

Sure, understand your point on this. Will try to fix this, and also provide
clear comments on how to add new ones.

Actually dfl_id maps to platform device name, and dfl_fpga_dev_type to char
device name. will try to find a way to do translations or just combine them
as currently each feature device (platform device) only need one chardev.

>
> > +
> > +/* it is protected by dfl_id_mutex */
> > +static struct idr dfl_ids[DFL_ID_MAX];
> > +
> > +static void dfl_ids_init(void)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(dfl_ids); i++)
> > + idr_init(dfl_ids + i);
> > +}
> > +
> > +static void dfl_ids_destroy(void)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < ARRAY_SIZE(dfl_ids); i++)
> > + idr_destroy(dfl_ids + i);
> > +}
> > +
> > +static int alloc_dfl_id(enum dfl_id_type type, struct device *dev)
> > +{
> > + int id;
> > +
> > + WARN_ON(type >= DFL_ID_MAX);
> > + mutex_lock(&dfl_id_mutex);
> > + id = idr_alloc(dfl_ids + type, dev, 0, 0, GFP_KERNEL);
> > + mutex_unlock(&dfl_id_mutex);
> > +
> > + return id;
> > +}
> > +
> > +static void free_dfl_id(enum dfl_id_type type, int id)
> > +{
> > + WARN_ON(type >= DFL_ID_MAX);
> > + mutex_lock(&dfl_id_mutex);
> > + idr_remove(dfl_ids + type, id);
> > + mutex_unlock(&dfl_id_mutex);
> > +}
>
> We discussed function name groups having a matching prefix on another
> thread for another patch. Same thing here, please, such as
> dfl_id_alloc/free.

Yes, will fix this in v6.

>
> > +
> > +static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
> > +{
> > + if (!strcmp(pdev->name, DFL_FPGA_FEATURE_DEV_FME))
> > + return FME_ID;
> > +
> > + if (!strcmp(pdev->name, DFL_FPGA_FEATURE_DEV_PORT))
> > + return PORT_ID;
>
> Will a new if statement need to be added to this function for each
> added DFH_ID? IIUC, at the point this function is called, the feature
> device exists and has private data. Perhaps it is worth saving the ID
> in its private data so you don't have to do this reverse lookup (and
> won't have to change this function for each new feature).
>
> Alternatively, if dfl_chrdevs[] is added in this patch, you could use
> it here, using a loop that goes through and does the lookup from that
> struct and you won't ever have to touch this function again.
>
> In either case, if you add the names of your devices to a table like
> dfl_chrdevs[], hopefully things can be coded such that words like
> DFL_FPGA_FEATURE_DEV_FME/PORT only have to show up in that table (and
> in the individual drivers).

Understand your point, will fix this.

>
> > +
> > + WARN_ON(1);
> > +
> > + return DFL_ID_MAX;
> > +}
> > +
> > +/**
> > + * struct 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.
> > + * @ioaddr: header register region address of feature device in enumeration.
> > + * @sub_features: a sub features link list for feature device in enumeration.
> > + * @feature_num: number of sub features for feature device in enumeration.
> > + */
> > +struct build_feature_devs_info {
> > + struct device *dev;
> > + struct dfl_fpga_cdev *cdev;
> > + struct platform_device *feature_dev;
> > + void __iomem *ioaddr;
> > + struct list_head sub_features;
> > + int feature_num;
> > +};
> > +
> > +/**
> > + * struct dfl_feature_info - sub feature info collected during feature dev build
> > + *
> > + * @fid: id of this sub feature.
> > + * @mmio_res: mmio resource of this sub feature.
> > + * @ioaddr: mapped base address of mmio resource.
> > + * @node: node in sub_features link list.
> > + */
> > +struct dfl_feature_info {
> > + u64 fid;
> > + struct resource mmio_res;
> > + void __iomem *ioaddr;
> > + struct list_head node;
> > +};
> > +
> > +static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev,
> > + struct platform_device *port)
> > +{
> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&port->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)
> > +{
> > + struct platform_device *fdev = binfo->feature_dev;
> > + struct dfl_feature_platform_data *pdata;
> > + struct dfl_feature_info *finfo, *p;
> > + int ret, index = 0;
> > +
> > + if (!fdev)
> > + return 0;
> > +
> > + /*
> > + * 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(dfl_feature_platform_data_size(binfo->feature_num),
> > + GFP_KERNEL);
> > + if (pdata) {
> > + pdata->dev = fdev;
> > + pdata->num = binfo->feature_num;
> > + pdata->dfl_cdev = binfo->cdev;
> > + mutex_init(&pdata->lock);
> > + } else {
> > + return -ENOMEM;
> > + }
>
> if (!pdata)
> return -ENOMEM;
>
> pdata->dev = fdev; so on

will fix this.

>
> > +
> > + /*
> > + * 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;
> > +
> > + /* each sub feature has one MMIO resource */
> > + fdev->num_resources = binfo->feature_num;
> > + fdev->resource = kcalloc(binfo->feature_num, sizeof(*fdev->resource),
> > + GFP_KERNEL);
> > + if (!fdev->resource)
> > + return -ENOMEM;
> > +
> > + /* fill features and resource information for feature dev */
> > + list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
> > + struct dfl_feature *feature = &pdata->features[index];
> > +
> > + /* save resource information for each feature */
> > + feature->id = finfo->fid;
> > + feature->resource_index = index;
> > + feature->ioaddr = finfo->ioaddr;
> > + fdev->resource[index++] = finfo->mmio_res;
> > +
> > + list_del(&finfo->node);
> > + kfree(finfo);
> > + }
> > +
> > + ret = platform_device_add(binfo->feature_dev);
> > + if (!ret) {
> > + if (feature_dev_id_type(binfo->feature_dev) == PORT_ID)
> > + dfl_fpga_cdev_add_port_dev(binfo->cdev,
> > + binfo->feature_dev);
> > + else
> > + 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 dfl_id_type type, const char *name,
>
> If dfl_chrdevs[] were moved to this patch, build_info_create_dev could
> use it to look up the name. That way you won't have to have separate
> functions for parse_feature_fme and parse_feature_port.

Yes, if we have the mapping table, then we don't need to pass the
related platform device name to this function. will fix this in v6.

>
> > + void __iomem *ioaddr)
> > +{
> > + struct platform_device *fdev;
> > + 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;
> > + binfo->feature_num = 0;
> > + binfo->ioaddr = ioaddr;
> > + INIT_LIST_HEAD(&binfo->sub_features);
> > +
> > + fdev->id = alloc_dfl_id(type, &fdev->dev);
> > + if (fdev->id < 0)
> > + return fdev->id;
> > +
> > + fdev->dev.parent = &binfo->cdev->region->dev;
> > +
> > + return 0;
> > +}
> > +
> > +static void build_info_free(struct build_feature_devs_info *binfo)
> > +{
> > + struct dfl_feature_info *finfo, *p;
> > +
> > + /*
> > + * it is a valid id, free it. See comments in
> > + * build_info_create_dev()
> > + */
> > + if (binfo->feature_dev && binfo->feature_dev->id >= 0) {
> > + free_dfl_id(feature_dev_id_type(binfo->feature_dev),
> > + binfo->feature_dev->id);
> > +
> > + list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
> > + list_del(&finfo->node);
> > + kfree(finfo);
> > + }
> > + }
> > +
> > + platform_device_put(binfo->feature_dev);
> > +
> > + devm_kfree(binfo->dev, binfo);
> > +}
> > +
> > +static inline u32 feature_size(void __iomem *start)
> > +{
> > + u64 v = readq(start + DFH);
> > + u32 ofst = FIELD_GET(DFH_NEXT_HDR_OFST, v);
> > + /* workaround for private features with invalid size, use 4K instead */
> > + return ofst ? ofst : 4096;
> > +}
> > +
> > +static u64 feature_id(void __iomem *start)
> > +{
> > + u64 v = readq(start + DFH);
> > + u16 id = FIELD_GET(DFH_ID, v);
> > + u8 type = FIELD_GET(DFH_TYPE, v);
> > +
> > + if (type == DFH_TYPE_FIU)
> > + return FEATURE_ID_FIU_HEADER;
> > + else if (type == DFH_TYPE_PRIVATE)
> > + return id;
> > + else if (type == DFH_TYPE_AFU)
> > + return FEATURE_ID_AFU;
> > +
> > + WARN_ON(1);
> > + return 0;
> > +}
> > +
> > +/*
> > + * when create sub feature instances, for private features, it doesn't need
> > + * to provide resource size and feature id as they could be read from DFH
> > + * register. For afu sub feature, its register region only contains user
> > + * defined registers, so never trust any information from it, just use the
> > + * resource size information provided by its parent FIU.
> > + */
> > +static int
> > +create_feature_instance(struct build_feature_devs_info *binfo,
> > + struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst,
> > + resource_size_t size, u64 fid)
> > +{
> > + struct dfl_feature_info *finfo;
> > +
> > + /* read feature size and id if inputs are invalid */
> > + size = size ? size : feature_size(dfl->ioaddr + ofst);
> > + fid = fid ? fid : feature_id(dfl->ioaddr + ofst);
> > +
> > + if (dfl->len - ofst < size)
> > + return -EINVAL;
> > +
> > + finfo = kzalloc(sizeof(*finfo), GFP_KERNEL);
> > + if (!finfo)
> > + return -ENOMEM;
> > +
> > + finfo->fid = fid;
> > + finfo->mmio_res.start = dfl->start + ofst;
> > + finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
> > + finfo->mmio_res.flags = IORESOURCE_MEM;
> > + finfo->ioaddr = dfl->ioaddr + ofst;
> > +
> > + list_add_tail(&finfo->node, &binfo->sub_features);
> > + binfo->feature_num++;
> > +
> > + return 0;
> > +}
> > +
> > +static int parse_feature_fme(struct build_feature_devs_info *binfo,
> > + struct dfl_fpga_enum_dfl *dfl,
> > + resource_size_t ofst)
> > +{
> > + int ret;
> > +
> > + ret = build_info_create_dev(binfo, FME_ID, DFL_FPGA_FEATURE_DEV_FME,
> > + dfl->ioaddr + ofst);
> > + if (ret)
> > + return ret;
> > +
> > + return create_feature_instance(binfo, dfl, ofst, 0, 0);
> > +}
> > +
> > +static int parse_feature_port(struct build_feature_devs_info *binfo,
> > + struct dfl_fpga_enum_dfl *dfl,
> > + resource_size_t ofst)
> > +{
> > + int ret;
> > +
> > + ret = build_info_create_dev(binfo, PORT_ID, DFL_FPGA_FEATURE_DEV_PORT,
> > + dfl->ioaddr + ofst);
> > + if (ret)
> > + return ret;
> > +
> > + return create_feature_instance(binfo, dfl, ofst, 0, 0);
> > +}
> > +
> > +static int parse_feature_port_afu(struct build_feature_devs_info *binfo,
> > + struct dfl_fpga_enum_dfl *dfl,
> > + resource_size_t ofst)
> > +{
> > + u64 v = readq(binfo->ioaddr + PORT_HDR_CAP);
> > + u32 size = FIELD_GET(PORT_CAP_MMIO_SIZE, v) << 10;
> > +
> > + WARN_ON(!size);
> > +
> > + return create_feature_instance(binfo, dfl, ofst, size, FEATURE_ID_AFU);
> > +}
> > +
> > +static int parse_feature_afu(struct build_feature_devs_info *binfo,
> > + struct dfl_fpga_enum_dfl *dfl,
> > + resource_size_t ofst)
> > +{
> > + if (!binfo->feature_dev) {
> > + dev_err(binfo->dev, "this AFU does not belong to any FIU.\n");
> > + return -EINVAL;
> > + }
> > +
> > + switch (feature_dev_id_type(binfo->feature_dev)) {
> > + case PORT_ID:
> > + return parse_feature_port_afu(binfo, dfl, ofst);
> > + default:
> > + dev_info(binfo->dev, "AFU belonging to FIU %s is not supported yet.\n",
> > + binfo->feature_dev->name);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int parse_feature_fiu(struct build_feature_devs_info *binfo,
> > + struct dfl_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:
> > + ret = parse_feature_fme(binfo, dfl, ofst);
> > + break;
> > + case DFH_ID_FIU_PORT:
> > + ret = parse_feature_port(binfo, dfl, ofst);
> > + break;
> > + default:
> > + dev_info(binfo->dev, "FIU TYPE %d is not supported yet.\n",
> > + id);
> > + }
>
> If the name lookup is added to build_info_create_dev(), then this
> switch statement goes away and the contents of parse_feature_fme/port
> are identical and trivial enough to be included here. My reason for
> looking for these things is to reduce, where possible, the places
> where a function needs to be added or changed to parse each new ID.

I see, will improve this.

Thanks
Hao

2018-06-06 12:36:48

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 07/28] fpga: dfl: add chardev support for feature devices

On Tue, Jun 05, 2018 at 03:21:48PM -0500, Alan Tull wrote:
> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>
> Hi Hao,
>
> > 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 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 <[email protected]>
> > Signed-off-by: Enno Luebbers <[email protected]>
> > Signed-off-by: Shiva Rao <[email protected]>
> > Signed-off-by: Christopher Rauer <[email protected]>
> > Signed-off-by: Zhang Yi <[email protected]>
> > Signed-off-by: Xiao Guangrong <[email protected]>
> > Signed-off-by: Wu Hao <[email protected]>
> > ---
> > v2: rebased
> > v3: move chardev support to fpga-dfl framework
> > v4: rebase, and add more comments in code.
> > v5: rebase, and add dfl_ prefix to APIs and data structures.
> > ---
> > drivers/fpga/dfl.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
> > drivers/fpga/dfl.h | 14 ++++++++
> > 2 files changed, 117 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> > index c1462e9..18aba02 100644
> > --- a/drivers/fpga/dfl.c
> > +++ b/drivers/fpga/dfl.c
> > @@ -74,6 +74,96 @@ static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
> > return DFL_ID_MAX;
> > }
> >
> > +struct dfl_chardev_info {
> > + const char *name;
> > + dev_t devt;
> > +};
> > +
> > +/* indexed by enum dfl_fpga_devt_type */
> > +struct dfl_chardev_info dfl_chrdevs[] = {
> > + {.name = DFL_FPGA_FEATURE_DEV_FME}, /* DFL_FPGA_DEVT_FME */
> > + {.name = DFL_FPGA_FEATURE_DEV_PORT}, /* DFL_FPGA_DEVT_AFU */
> > +};
>
> If this were added in the initial dfl.c patch, it could be used by
> build_info_create_dev to get the name.

Sure, will fix this.

>
> > +
> > +static void dfl_chardev_uinit(void)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < DFL_FPGA_DEVT_MAX; i++)
> > + if (MAJOR(dfl_chrdevs[i].devt)) {
> > + unregister_chrdev_region(dfl_chrdevs[i].devt,
> > + MINORMASK);
> > + dfl_chrdevs[i].devt = MKDEV(0, 0);
> > + }
> > +}
> > +
> > +static int dfl_chardev_init(void)
> > +{
> > + int i, ret;
> > +
> > + for (i = 0; i < DFL_FPGA_DEVT_MAX; i++) {
> > + ret = alloc_chrdev_region(&dfl_chrdevs[i].devt, 0, MINORMASK,
> > + dfl_chrdevs[i].name);
> > + if (ret)
> > + goto exit;
> > + }
> > +
> > + return 0;
> > +
> > +exit:
> > + dfl_chardev_uinit();
> > + return ret;
> > +}
> > +
> > +static dev_t dfl_get_devt(enum dfl_fpga_devt_type type, int id)
> > +{
> > + WARN_ON(type >= DFL_FPGA_DEVT_MAX);
> > +
> > + return MKDEV(MAJOR(dfl_chrdevs[type].devt), id);
> > +}
> > +
> > +/**
> > + * dfl_fpga_register_dev_ops - register cdev ops for feature dev
> > + *
> > + * @pdev: feature dev.
> > + * @fops: file operations for feature dev's cdev.
> > + * @owner: owning module/driver.
> > + *
> > + * Return: 0 on success, negative error code otherwise.
> > + */
> > +int dfl_fpga_register_dev_ops(struct platform_device *pdev,
> > + const struct file_operations *fops,
> > + struct module *owner)
> > +{
> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> > +
> > + cdev_init(&pdata->cdev, fops);
> > + pdata->cdev.owner = owner;
> > +
> > + /*
> > + * set parent to the feature device so that its refcount is
> > + * decreased after the last refcount of cdev is gone, that
> > + * makes sure the feature device is valid during device
> > + * file's life-cycle.
> > + */
> > + pdata->cdev.kobj.parent = &pdev->dev.kobj;
> > +
> > + return cdev_add(&pdata->cdev, pdev->dev.devt, 1);
> > +}
> > +EXPORT_SYMBOL_GPL(dfl_fpga_register_dev_ops);
> > +
> > +/**
> > + * dfl_fpga_unregister_dev_ops - unregister cdev ops for feature dev
> > + * @pdev: feature dev.
> > + */
> > +void dfl_fpga_unregister_dev_ops(struct platform_device *pdev)
> > +{
> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> > +
> > + cdev_del(&pdata->cdev);
> > +}
> > +EXPORT_SYMBOL_GPL(dfl_fpga_unregister_dev_ops);
> > +
> > /**
> > * struct build_feature_devs_info - info collected during feature dev build.
> > *
> > @@ -208,9 +298,13 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
> > enum dfl_id_type type, const char *name,
> > void __iomem *ioaddr)
> > {
> > + enum dfl_fpga_devt_type devt_type = DFL_FPGA_DEVT_FME;
> > struct platform_device *fdev;
> > int ret;
> >
> > + if (type == PORT_ID)
> > + devt_type = DFL_FPGA_DEVT_PORT;
> > +
> > /* we will create a new device, commit current device first */
> > ret = build_info_commit_dev(binfo);
> > if (ret)
> > @@ -234,6 +328,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
> > return fdev->id;
> >
> > fdev->dev.parent = &binfo->cdev->region->dev;
> > + fdev->dev.devt = dfl_get_devt(devt_type, fdev->id);
> >
> > return 0;
> > }
> > @@ -702,13 +797,20 @@ void dfl_fpga_remove_feature_devs(struct dfl_fpga_cdev *cdev)
> >
> > static int __init dfl_fpga_init(void)
> > {
> > + int ret;
> > +
> > dfl_ids_init();
> >
> > - return 0;
> > + ret = dfl_chardev_init();
> > + if (ret)
> > + dfl_ids_destroy();
> > +
> > + return ret;
> > }
> >
> > static void __exit dfl_fpga_exit(void)
> > {
> > + dfl_chardev_uinit();
> > dfl_ids_destroy();
> > }
> >
> > diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
> > index 2ede915..5fcb1a1 100644
> > --- a/drivers/fpga/dfl.h
> > +++ b/drivers/fpga/dfl.h
> > @@ -15,6 +15,7 @@
> > #define __FPGA_DFL_H
> >
> > #include <linux/bitfield.h>
> > +#include <linux/cdev.h>
> > #include <linux/delay.h>
> > #include <linux/fs.h>
> > #include <linux/iopoll.h>
> > @@ -150,6 +151,7 @@ struct dfl_feature {
> > *
> > * @node: node to link feature devs to container device's port_dev_list.
> > * @lock: mutex to protect platform data.
> > + * @cdev: cdev of feature dev.
> > * @dev: ptr to platform device linked with this platform data.
> > * @dfl_cdev: ptr to container device.
> > * @disable_count: count for port disable.
> > @@ -159,6 +161,7 @@ struct dfl_feature {
> > struct dfl_feature_platform_data {
> > struct list_head node;
> > struct mutex lock;
> > + struct cdev cdev;
> > struct platform_device *dev;
> > struct dfl_fpga_cdev *dfl_cdev;
> > unsigned int disable_count;
> > @@ -176,6 +179,17 @@ static inline int dfl_feature_platform_data_size(const int num)
> > num * sizeof(struct dfl_feature);
> > }
> >
> > +enum dfl_fpga_devt_type {
> > + DFL_FPGA_DEVT_FME,
> > + DFL_FPGA_DEVT_PORT,
> > + DFL_FPGA_DEVT_MAX,
> > +};
>
> Could you move this enum to be close to other similar enums (like
> dfl_id_type)? The dfl code has a few enums that are similar, and may
> need updating (or not) as feature are added. Putting them close
> together with appropriate comments would be helpful to keep them all
> straight.

Agree, I will fix this in v6.
Thanks for the review.

Hao

2018-06-06 12:46:40

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 09/28] fpga: dfl: add feature device infrastructure

On Tue, Jun 05, 2018 at 04:14:43PM -0500, Alan Tull wrote:
> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>
> Hi Hao,
>
> Some minor things, otherwise looks fine.
>
> > From: Xiao Guangrong <[email protected]>
> >
> > 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 dfl_feature_dev_use_begin/end which are used to ensure
> > exclusive usage of the feature device file.
> >
> > Signed-off-by: Tim Whisonant <[email protected]>
> > Signed-off-by: Enno Luebbers <[email protected]>
> > Signed-off-by: Shiva Rao <[email protected]>
> > Signed-off-by: Christopher Rauer <[email protected]>
> > Signed-off-by: Kang Luwei <[email protected]>
> > Signed-off-by: Zhang Yi <[email protected]>
> > Signed-off-by: Xiao Guangrong <[email protected]>
> > Signed-off-by: Wu Hao <[email protected]>
> Acked-by: Alan Tull <[email protected]>
>
> > ---
> > v2: rebased
> > v3: use const for feature_ops.
> > replace pci related function.
> > v4: rebase and add more comments in code.
> > v5: remove useless WARN_ON().
> > reorder declarations in functions per suggestion from Moritz.
> > add "dfl_" prefix to functions and data structure.
> > ---
> > drivers/fpga/dfl.c | 57 +++++++++++++++++++++++++++++++++++++
> > drivers/fpga/dfl.h | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> > 2 files changed, 138 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c
> > index 1e06efb..c4c47d6 100644
> > --- a/drivers/fpga/dfl.c
> > +++ b/drivers/fpga/dfl.c
> > @@ -74,6 +74,63 @@ static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
> > return DFL_ID_MAX;
> > }
> >
> > +void dfl_fpga_dev_feature_uinit(struct platform_device *pdev)
> > +{
> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> > + struct dfl_feature *feature;
> > +
> > + dfl_fpga_dev_for_each_feature(pdata, feature)
> > + if (feature->ops) {
> > + feature->ops->uinit(pdev, feature);
> > + feature->ops = NULL;
> > + }
> > +}
> > +EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit);
>
> Please add kernel-doc for functions that are exported to recommend and
> guide their usage.

Sorry, I missed uinit and init functions. will add them in v6.

>
> > +
> > +static int dfl_feature_instance_init(struct platform_device *pdev,
> > + struct dfl_feature_platform_data *pdata,
> > + struct dfl_feature *feature,
> > + struct dfl_feature_driver *drv)
> > +{
> > + int ret;
> > +
> > + ret = drv->ops->init(pdev, feature);
> > + if (ret)
> > + return ret;
> > +
> > + feature->ops = drv->ops;
> > +
> > + return ret;
> > +}
> > +
> > +int dfl_fpga_dev_feature_init(struct platform_device *pdev,
> > + struct dfl_feature_driver *feature_drvs)
> > +{
> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> > + struct dfl_feature_driver *drv = feature_drvs;
> > + struct dfl_feature *feature;
> > + int ret;
> > +
> > + while (drv->ops) {
> > + dfl_fpga_dev_for_each_feature(pdata, feature) {
> > + /* match feature and drv using id */
> > + if (feature->id == drv->id) {
> > + ret = dfl_feature_instance_init(pdev, pdata,
> > + feature, drv);
> > + if (ret)
> > + goto exit;
> > + }
> > + }
> > + drv++;
> > + }
> > +
> > + return 0;
> > +exit:
> > + dfl_fpga_dev_feature_uinit(pdev);
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_init);
> > +
> > struct dfl_chardev_info {
> > const char *name;
> > dev_t devt;
> > diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h
> > index 2b6aaef..27f7a74 100644
> > --- a/drivers/fpga/dfl.h
> > +++ b/drivers/fpga/dfl.h
> > @@ -132,6 +132,17 @@
> > #define PORT_CTRL_SFTRST_ACK BIT_ULL(4) /* HW ack for reset */
> >
> > /**
> > + * struct dfl_feature_driver - sub feature's driver
> > + *
> > + * @id: sub feature id.
> > + * @ops: ops of this sub feature.
> > + */
> > +struct dfl_feature_driver {
> > + u64 id;
> > + const struct dfl_feature_ops *ops;
> > +};
> > +
> > +/**
> > * struct dfl_feature - sub feature of the feature devices
> > *
> > * @id: sub feature id.
> > @@ -139,13 +150,17 @@
> > * this index is used to find its mmio resource from the
> > * feature dev (platform device)'s reources.
> > * @ioaddr: mapped mmio resource address.
> > + * @ops: ops of this sub feature.
> > */
> > struct dfl_feature {
> > u64 id;
> > int resource_index;
> > void __iomem *ioaddr;
> > + const struct dfl_feature_ops *ops;
> > };
> >
> > +#define DEV_STATUS_IN_USE 0
> > +
> > /**
> > * struct dfl_feature_platform_data - platform data for feature devices
> > *
> > @@ -156,6 +171,8 @@ struct dfl_feature {
> > * @dfl_cdev: ptr to container device.
> > * @disable_count: count for port disable.
> > * @num: number for sub features.
> > + * @dev_status: dev status (e.g DEV_STATUS_IN_USE).
> > + * @private: ptr to feature dev private data.
> > * @features: sub features of this feature dev.
> > */
> > struct dfl_feature_platform_data {
> > @@ -165,11 +182,49 @@ struct dfl_feature_platform_data {
> > struct platform_device *dev;
> > struct dfl_fpga_cdev *dfl_cdev;
> > unsigned int disable_count;
> > -
> > + unsigned long dev_status;
> > + void *private;
> > int num;
> > struct dfl_feature features[0];
> > };
> >
> > +static inline
> > +int dfl_feature_dev_use_begin(struct dfl_feature_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;
> > +
> > + return 0;
> > +}
> > +
> > +static inline
> > +void dfl_feature_dev_use_end(struct dfl_feature_platform_data *pdata)
> > +{
> > + clear_bit_unlock(DEV_STATUS_IN_USE, &pdata->dev_status);
> > +}
> > +
> > +static inline
> > +void dfl_fpga_pdata_set_private(struct dfl_feature_platform_data *pdata,
> > + void *private)
> > +{
> > + pdata->private = private;
> > +}
> > +
> > +static inline
> > +void *dfl_fpga_pdata_get_private(struct dfl_feature_platform_data *pdata)
> > +{
> > + return pdata->private;
> > +}
> > +
> > +struct dfl_feature_ops {
> > + int (*init)(struct platform_device *pdev, struct dfl_feature *feature);
> > + void (*uinit)(struct platform_device *pdev,
> > + struct dfl_feature *feature);
> > + long (*ioctl)(struct platform_device *pdev, struct dfl_feature *feature,
> > + unsigned int cmd, unsigned long arg);
> > +};
> > +
> > #define DFL_FPGA_FEATURE_DEV_FME "dfl-fme"
> > #define DFL_FPGA_FEATURE_DEV_PORT "dfl-port"
>
> Please move these to the same place as other things that will need to
> be added to as feature devices are added as noted in the other reviews
> today.

as these two strings are used as platform device name, so I think we need to
keep them in the dfl.h file, because platform driver could reuse the same.
But I will add detailed comments to guide others to put name string for new
feature device (platform device) in the dfl.h file together with above ones.

Thanks a lot for the review.
Hao

2018-06-06 15:54:07

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 18/28] fpga: dfl: add fpga manager platform driver for FME

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> This patch adds fpga manager driver for FPGA Management Engine (FME). It
> implements fpga_manager_ops for FPGA Partial Reconfiguration function.
>
> Signed-off-by: Tim Whisonant <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Kang Luwei <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>

Acked-by: Alan Tull <[email protected]>

> ---
> v3: rename driver to dfl-fpga-fme-mgr
> implemented status callback for fpga manager
> rebased due to fpga api changes
> v4: rename to dfl-fme-mgr, and fix SPDX license issue
> add pr_credit comments and improve dev_err message
> remove interface_id sysfs interface
> include dfl-fme-pr.h instead of dfl.h
> v5: move related register definition from dfl-fme-pr.h to dfl-fme-mgr.c
> use mapped ioaddr passed via pdata for register access.
> rebase due to fpga manager API changes.
> include header file for readq/writeq.
> ---
> drivers/fpga/Kconfig | 6 +
> drivers/fpga/Makefile | 1 +
> drivers/fpga/dfl-fme-mgr.c | 334 +++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 341 insertions(+)
> create mode 100644 drivers/fpga/dfl-fme-mgr.c
>
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 103d5e2..89f76e8 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -150,6 +150,12 @@ config FPGA_DFL_FME
> FPGA platform level management features. There shall be 1 FME
> per DFL based FPGA device.
>
> +config FPGA_DFL_FME_MGR
> + tristate "FPGA DFL FME Manager Driver"
> + depends on FPGA_DFL_FME
> + help
> + Say Y to enable FPGA Manager driver for FPGA Management Engine.
> +
> config FPGA_DFL_PCI
> tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
> depends on PCI && FPGA_DFL
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 3c44fc9..f82814a 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> # FPGA Device Feature List Support
> obj-$(CONFIG_FPGA_DFL) += dfl.o
> obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
> +obj-$(CONFIG_FPGA_DFL_FME_MGR) += dfl-fme-mgr.o
>
> dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
>
> diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c
> new file mode 100644
> index 0000000..1c5bc5a
> --- /dev/null
> +++ b/drivers/fpga/dfl-fme-mgr.c
> @@ -0,0 +1,334 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * FPGA Manager Driver for FPGA Management Engine (FME)
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + * Kang Luwei <[email protected]>
> + * Xiao Guangrong <[email protected]>
> + * Wu Hao <[email protected]>
> + * Joseph Grecco <[email protected]>
> + * Enno Luebbers <[email protected]>
> + * Tim Whisonant <[email protected]>
> + * Ananda Ravuri <[email protected]>
> + * Christopher Rauer <[email protected]>
> + * Henry Mitchel <[email protected]>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/module.h>
> +#include <linux/iopoll.h>
> +#include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/fpga/fpga-mgr.h>
> +
> +#include "dfl-fme-pr.h"
> +
> +/* FME Partial Reconfiguration Sub Feature Register Set */
> +#define FME_PR_DFH 0x0
> +#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
> +
> +/* FME PR Control Register Bitfield */
> +#define FME_PR_CTRL_PR_RST BIT_ULL(0) /* Reset PR engine */
> +#define FME_PR_CTRL_PR_RSTACK BIT_ULL(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_ULL(12) /* Start to request PR service */
> +#define FME_PR_CTRL_PR_COMPLETE BIT_ULL(13) /* PR data push completion */
> +
> +/* 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_ULL(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 */
> +/* PR Operation errors detected. */
> +#define FME_PR_ERR_OPERATION_ERR BIT_ULL(0)
> +/* CRC error detected. */
> +#define FME_PR_ERR_CRC_ERR BIT_ULL(1)
> +/* Incompatible PR bitstream detected. */
> +#define FME_PR_ERR_INCOMPATIBLE_BS BIT_ULL(2)
> +/* PR data push protocol violated. */
> +#define FME_PR_ERR_PROTOCOL_ERR BIT_ULL(3)
> +/* PR data fifo overflow error detected */
> +#define FME_PR_ERR_FIFO_OVERFLOW BIT_ULL(4)
> +
> +#define PR_WAIT_TIMEOUT 8000000
> +#define PR_HOST_STATUS_IDLE 0
> +
> +struct fme_mgr_priv {
> + void __iomem *ioaddr;
> + u64 pr_error;
> +};
> +
> +static u64 pr_error_to_mgr_status(u64 err)
> +{
> + u64 status = 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;
> +
> + return status;
> +}
> +
> +static u64 fme_mgr_pr_error_handle(void __iomem *fme_pr)
> +{
> + u64 pr_status, pr_error;
> +
> + pr_status = readq(fme_pr + FME_PR_STS);
> + if (!(pr_status & FME_PR_STS_PR_STS))
> + return 0;
> +
> + pr_error = readq(fme_pr + FME_PR_ERR);
> + writeq(pr_error, fme_pr + FME_PR_ERR);
> +
> + return pr_error;
> +}
> +
> +static int fme_mgr_write_init(struct fpga_manager *mgr,
> + struct fpga_image_info *info,
> + const char *buf, size_t count)
> +{
> + struct device *dev = &mgr->dev;
> + struct fme_mgr_priv *priv = mgr->priv;
> + void __iomem *fme_pr = priv->ioaddr;
> + u64 pr_ctrl, pr_status;
> +
> + if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
> + dev_err(dev, "only supports 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, "PR Reset ACK 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, "PR Status 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",
> + (unsigned long long)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);
> +
> + return 0;
> +}
> +
> +static int fme_mgr_write(struct fpga_manager *mgr,
> + const char *buf, size_t count)
> +{
> + struct device *dev = &mgr->dev;
> + struct fme_mgr_priv *priv = mgr->priv;
> + void __iomem *fme_pr = priv->ioaddr;
> + u64 pr_ctrl, pr_status, pr_data;
> + int delay = 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");
> +
> + /*
> + * driver can push data to PR hardware using PR_DATA register once HW
> + * has enough pr_credit (> 1), pr_credit reduces one for every 32bit
> + * pr data write to PR_DATA register. If pr_credit <= 1, driver needs
> + * to wait for enough pr_credit from hardware by polling.
> + */
> + 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, "PR_CREDIT timeout\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) {
> + WARN_ON(1);
> + return -EINVAL;
> + }
> +
> + 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++;
> + }
> +
> + return 0;
> +}
> +
> +static int fme_mgr_write_complete(struct fpga_manager *mgr,
> + struct fpga_image_info *info)
> +{
> + struct device *dev = &mgr->dev;
> + struct fme_mgr_priv *priv = mgr->priv;
> + void __iomem *fme_pr = priv->ioaddr;
> + u64 pr_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, "PR Completion ACK timeout.\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",
> + (unsigned long long)priv->pr_error);
> + return -EIO;
> + }
> +
> + dev_dbg(dev, "PR done successfully\n");
> +
> + return 0;
> +}
> +
> +static enum fpga_mgr_states fme_mgr_state(struct fpga_manager *mgr)
> +{
> + return FPGA_MGR_STATE_UNKNOWN;
> +}
> +
> +static u64 fme_mgr_status(struct fpga_manager *mgr)
> +{
> + struct fme_mgr_priv *priv = mgr->priv;
> +
> + return pr_error_to_mgr_status(priv->pr_error);
> +}
> +
> +static const struct fpga_manager_ops fme_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,
> +};
> +
> +static int fme_mgr_probe(struct platform_device *pdev)
> +{
> + struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev);
> + struct device *dev = &pdev->dev;
> + struct fme_mgr_priv *priv;
> + struct fpga_manager *mgr;
> + struct resource *res;
> + int ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> +
> + if (pdata->ioaddr)
> + priv->ioaddr = pdata->ioaddr;
> +
> + if (!priv->ioaddr) {
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->ioaddr = devm_ioremap_resource(dev, res);
> + if (IS_ERR(priv->ioaddr))
> + return PTR_ERR(priv->ioaddr);
> + }
> +
> + mgr = fpga_mgr_create(dev, "DFL FME FPGA Manager",
> + &fme_mgr_ops, priv);
> + if (!mgr)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, mgr);
> +
> + ret = fpga_mgr_register(mgr);
> + if (ret)
> + fpga_mgr_free(mgr);
> +
> + return ret;
> +}
> +
> +static int fme_mgr_remove(struct platform_device *pdev)
> +{
> + struct fpga_manager *mgr = platform_get_drvdata(pdev);
> +
> + fpga_mgr_unregister(mgr);
> +
> + return 0;
> +}
> +
> +static struct platform_driver fme_mgr_driver = {
> + .driver = {
> + .name = DFL_FPGA_FME_MGR,
> + },
> + .probe = fme_mgr_probe,
> + .remove = fme_mgr_remove,
> +};
> +
> +module_platform_driver(fme_mgr_driver);
> +
> +MODULE_DESCRIPTION("FPGA Manager for DFL FPGA Management Engine");
> +MODULE_AUTHOR("Intel Corporation");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dfl-fme-mgr");
> --
> 1.8.3.1
>

2018-06-06 16:00:45

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 24/28] fpga: dfl: afu: add port ops support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> This patch registers the port ops into the global list in the DFL
> framework, and it allows other modules to use the port ops. And
> This patch includes the implementation of the get_id and enable_set
> ops too.
>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> drivers/fpga/dfl-afu-main.c | 122 +++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 121 insertions(+), 1 deletion(-)

2018-06-06 16:06:07

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 27/28] fpga: dfl: afu: add afu sub feature support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> From: Xiao Guangrong <[email protected]>
>
> User Accelerated Function Unit sub feature exposes the MMIO region of
> the AFU. After valid PR bitstream is programmed and the 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>/<dfl-port.x>/afu_id
> Read-only. Indicate which PR bitstream is programmed to this AFU.
>
> Ioctl interfaces:
> * DFL_FPGA_PORT_GET_INFO
> Provide info to userspace on the number of supported region.
> Only UAFU region is supported now.
>
> * DFL_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 <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

2018-06-06 16:15:57

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 17/28] fpga: dfl: fme: add partial reconfiguration sub feature support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

My patches that change the API for creating managers, bridges, and
regions is in Greg's char-misc-next branch now, so that will hopefully
be in v4.18. You'll need to adjust for that, but that will be minor.

> From: Kang Luwei <[email protected]>
>
> 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:
> * DFL_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 <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Kang Luwei <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> 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.
> v4: rebase and correct comments for some function.
> fix SPDX license issue.
> remove unnecessary input parameter for destroy_bridge/region function.
> add dfl-fme-pr.h for PR sub feature data structure and registers.
> v5: rebase due to DFL framework API naming changes.
> improve naming for IOCTL API, functions and data structure.
> defer finding port platform device to fme bridge.
> pass mapped ioaddr to fme manager via pdata.
> remove useless devm_kfree().
> ---
> drivers/fpga/Makefile | 2 +-
> drivers/fpga/dfl-fme-main.c | 43 +++-
> drivers/fpga/dfl-fme-pr.c | 474 ++++++++++++++++++++++++++++++++++++++++++
> drivers/fpga/dfl-fme-pr.h | 84 ++++++++
> drivers/fpga/dfl-fme.h | 38 ++++
> include/uapi/linux/fpga-dfl.h | 28 +++
> 6 files changed, 667 insertions(+), 2 deletions(-)
> create mode 100644 drivers/fpga/dfl-fme-pr.c
> create mode 100644 drivers/fpga/dfl-fme-pr.h
> create mode 100644 drivers/fpga/dfl-fme.h
>
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index fbd1c85..3c44fc9 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -32,7 +32,7 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
> obj-$(CONFIG_FPGA_DFL) += dfl.o
> obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
>
> -dfl-fme-objs := dfl-fme-main.o
> +dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
>
> # Drivers for FPGAs which implement DFL
> obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
> diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
> index 6a59c07..bcb9325 100644
> --- a/drivers/fpga/dfl-fme-main.c
> +++ b/drivers/fpga/dfl-fme-main.c
> @@ -19,6 +19,7 @@
> #include <linux/fpga-dfl.h>
>
> #include "dfl.h"
> +#include "dfl-fme.h"
>
> static ssize_t ports_num_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> @@ -113,6 +114,10 @@ static ssize_t bitstream_metadata_show(struct device *dev,
> .ops = &fme_hdr_ops,
> },
> {
> + .id = FME_FEATURE_ID_PR_MGMT,
> + .ops = &pr_mgmt_ops,
> + },
> + {
> .ops = NULL,
> },
> };
> @@ -187,6 +192,35 @@ static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> return -EINVAL;
> }
>
> +static int fme_dev_init(struct platform_device *pdev)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> + struct dfl_fme *fme;
> +
> + fme = devm_kzalloc(&pdev->dev, sizeof(*fme), GFP_KERNEL);
> + if (!fme)
> + return -ENOMEM;
> +
> + fme->pdata = pdata;
> +
> + mutex_lock(&pdata->lock);
> + dfl_fpga_pdata_set_private(pdata, fme);
> + mutex_unlock(&pdata->lock);
> +
> + return 0;
> +}
> +
> +static void fme_dev_destroy(struct platform_device *pdev)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> + struct dfl_fme *fme;
> +
> + mutex_lock(&pdata->lock);
> + fme = dfl_fpga_pdata_get_private(pdata);
> + dfl_fpga_pdata_set_private(pdata, NULL);
> + mutex_unlock(&pdata->lock);
> +}
> +
> static const struct file_operations fme_fops = {
> .owner = THIS_MODULE,
> .open = fme_open,
> @@ -198,10 +232,14 @@ static int fme_probe(struct platform_device *pdev)
> {
> int ret;
>
> - ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs);
> + ret = fme_dev_init(pdev);
> if (ret)
> goto exit;
>
> + ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs);
> + if (ret)
> + goto dev_destroy;
> +
> ret = dfl_fpga_register_dev_ops(pdev, &fme_fops, THIS_MODULE);
> if (ret)
> goto feature_uinit;
> @@ -210,6 +248,8 @@ static int fme_probe(struct platform_device *pdev)
>
> feature_uinit:
> dfl_fpga_dev_feature_uinit(pdev);
> +dev_destroy:
> + fme_dev_destroy(pdev);
> exit:
> return ret;
> }
> @@ -218,6 +258,7 @@ static int fme_remove(struct platform_device *pdev)
> {
> dfl_fpga_unregister_dev_ops(pdev);
> dfl_fpga_dev_feature_uinit(pdev);
> + fme_dev_destroy(pdev);
>
> return 0;
> }
> diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c
> new file mode 100644
> index 0000000..3d5f8b9
> --- /dev/null
> +++ b/drivers/fpga/dfl-fme-pr.c
> @@ -0,0 +1,474 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for FPGA Management Engine (FME) Partial Reconfiguration
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + * Kang Luwei <[email protected]>
> + * Xiao Guangrong <[email protected]>
> + * Wu Hao <[email protected]>
> + * Joseph Grecco <[email protected]>
> + * Enno Luebbers <[email protected]>
> + * Tim Whisonant <[email protected]>
> + * Ananda Ravuri <[email protected]>
> + * Christopher Rauer <[email protected]>
> + * Henry Mitchel <[email protected]>
> + */
> +
> +#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 "dfl.h"
> +#include "dfl-fme.h"
> +#include "dfl-fme-pr.h"
> +
> +static struct dfl_fme_region *
> +find_fme_region_by_port_id(struct dfl_fme *fme, int port_id)

How about dfl_fme_region_find_by_port_id?

> +{
> + struct dfl_fme_region *fme_region;
> +
> + list_for_each_entry(fme_region, &fme->region_list, node)
> + if (fme_region->port_id == port_id)
> + return fme_region;
> +
> + return NULL;
> +}
> +
> +static int dfl_fme_region_match(struct device *dev, const void *data)
> +{
> + return dev->parent == data;
> +}
> +
> +static struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id)
> +{
> + struct dfl_fme_region *fme_region;
> + struct fpga_region *region;
> +
> + fme_region = find_fme_region_by_port_id(fme, port_id);
> + if (!fme_region)
> + return NULL;
> +
> + region = fpga_region_class_find(NULL, &fme_region->region->dev,
> + dfl_fme_region_match);
> + if (!region)
> + return NULL;
> +
> + return region;
> +}
> +
> +static int fme_pr(struct platform_device *pdev, unsigned long arg)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> + void __user *argp = (void __user *)arg;
> + struct dfl_fpga_fme_port_pr port_pr;
> + struct fpga_image_info *info;
> + struct fpga_region *region;
> + void __iomem *fme_hdr;
> + struct dfl_fme *fme;
> + unsigned long minsz;
> + void *buf = NULL;
> + int ret = 0;
> + u64 v;
> +
> + minsz = offsetofend(struct dfl_fpga_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 = dfl_get_feature_ioaddr_by_id(&pdev->dev,
> + FME_FEATURE_ID_HEADER);
> +
> + /* 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 *)(unsigned long)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 *)(unsigned long)port_pr.buffer_address,
> + port_pr.buffer_size)) {
> + ret = -EFAULT;
> + goto free_exit;
> + }
> +
> + /* prepare fpga_image_info for PR */
> + info = fpga_image_info_alloc(&pdev->dev);
> + if (!info) {
> + ret = -ENOMEM;
> + goto free_exit;
> + }
> +
> + info->flags |= FPGA_MGR_PARTIAL_RECONFIG;
> +
> + mutex_lock(&pdata->lock);
> + fme = dfl_fpga_pdata_get_private(pdata);
> + /* fme device has been unregistered. */
> + if (!fme) {
> + ret = -EINVAL;
> + goto unlock_exit;
> + }
> +
> + region = dfl_fme_region_find(fme, port_pr.port_id);
> + if (!region) {
> + ret = -EINVAL;
> + goto unlock_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(&region->bridge_list);
> +
> + put_device(&region->dev);
> +unlock_exit:
> + mutex_unlock(&pdata->lock);
> +free_exit:
> + vfree(buf);
> + if (copy_to_user((void __user *)arg, &port_pr, minsz))
> + return -EFAULT;
> +
> + return ret;
> +}
> +
> +/**
> + * dfl_fme_create_mgr - create fpga mgr platform device as child device
> + *
> + * @pdata: fme platform_device's pdata
> + *
> + * Return: mgr platform device if successful, and error code otherwise.
> + */
> +static struct platform_device *
> +dfl_fme_create_mgr(struct dfl_feature_platform_data *pdata,
> + struct dfl_feature *feature)
> +{
> + struct platform_device *mgr, *fme = pdata->dev;
> + struct dfl_fme_mgr_pdata mgr_pdata;
> + int ret = -ENOMEM;
> +
> + if (!feature->ioaddr)
> + return ERR_PTR(-ENODEV);
> +
> + mgr_pdata.ioaddr = feature->ioaddr;
> +
> + /*
> + * Each FME has only one fpga-mgr, so allocate platform device using
> + * the same FME platform device id.
> + */
> + mgr = platform_device_alloc(DFL_FPGA_FME_MGR, fme->id);
> + if (!mgr)
> + return ERR_PTR(ret);
> +
> + mgr->dev.parent = &fme->dev;
> +
> + ret = platform_device_add_data(mgr, &mgr_pdata, sizeof(mgr_pdata));
> + if (ret)
> + goto create_mgr_err;
> +
> + ret = platform_device_add(mgr);
> + if (ret)
> + goto create_mgr_err;
> +
> + return mgr;
> +
> +create_mgr_err:
> + platform_device_put(mgr);
> + return ERR_PTR(ret);
> +}
> +
> +/**
> + * dfl_fme_destroy_mgr - destroy fpga mgr platform device
> + * @pdata: fme platform device's pdata
> + */
> +static void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata)
> +{
> + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
> +
> + platform_device_unregister(priv->mgr);
> +}
> +
> +/**
> + * dfl_fme_create_bridge - create fme fpga bridge platform device as child
> + *
> + * @pdata: fme platform device's pdata
> + * @port_id: port id for the bridge to be created.
> + *
> + * Return: bridge platform device if successful, and error code otherwise.
> + */
> +static struct dfl_fme_bridge *
> +dfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id)
> +{
> + struct device *dev = &pdata->dev->dev;
> + struct dfl_fme_br_pdata br_pdata;
> + struct dfl_fme_bridge *fme_br;
> + int ret = -ENOMEM;
> +
> + fme_br = devm_kzalloc(dev, sizeof(*fme_br), GFP_KERNEL);
> + if (!fme_br)
> + return ERR_PTR(ret);
> +
> + br_pdata.cdev = pdata->dfl_cdev;
> + br_pdata.port_id = port_id;
> +
> + fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE,
> + PLATFORM_DEVID_AUTO);
> + if (!fme_br->br)
> + return ERR_PTR(ret);
> +
> + fme_br->br->dev.parent = dev;
> +
> + ret = platform_device_add_data(fme_br->br, &br_pdata, sizeof(br_pdata));
> + if (ret)
> + goto create_br_err;
> +
> + ret = platform_device_add(fme_br->br);
> + if (ret)
> + goto create_br_err;
> +
> + return fme_br;
> +
> +create_br_err:
> + platform_device_put(fme_br->br);
> + return ERR_PTR(ret);
> +}
> +
> +/**
> + * dfl_fme_destroy_bridge - destroy fpga bridge platform device
> + * @fme_br: fme bridge to destroy
> + */
> +static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br)
> +{
> + platform_device_unregister(fme_br->br);
> +}
> +
> +/**
> + * dfl_fme_destroy_bridge - destroy all fpga bridge platform device
> + * @pdata: fme platform device's pdata
> + */
> +static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata)
> +{
> + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
> + struct dfl_fme_bridge *fbridge, *tmp;
> +
> + list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) {
> + list_del(&fbridge->node);
> + dfl_fme_destroy_bridge(fbridge);
> + }
> +}
> +
> +/**
> + * dfl_fme_create_region - create fpga region platform device as child
> + *
> + * @pdata: fme platform device's pdata
> + * @mgr: mgr platform device needed for region
> + * @br: br platform device needed for region
> + * @port_id: port id
> + *
> + * Return: fme region if successful, and error code otherwise.
> + */
> +static struct dfl_fme_region *
> +dfl_fme_create_region(struct dfl_feature_platform_data *pdata,
> + struct platform_device *mgr,
> + struct platform_device *br, int port_id)
> +{
> + struct dfl_fme_region_pdata region_pdata;
> + struct device *dev = &pdata->dev->dev;
> + struct dfl_fme_region *fme_region;
> + int ret = -ENOMEM;
> +
> + fme_region = devm_kzalloc(dev, sizeof(*fme_region), GFP_KERNEL);
> + if (!fme_region)
> + return ERR_PTR(ret);
> +
> + region_pdata.mgr = mgr;
> + region_pdata.br = br;
> +
> + /*
> + * Each FPGA device may have more than one port, so allocate platform
> + * device using the same port platform device id.
> + */
> + fme_region->region = platform_device_alloc(DFL_FPGA_FME_REGION, br->id);
> + if (!fme_region->region)
> + return ERR_PTR(ret);
> +
> + fme_region->region->dev.parent = dev;
> +
> + ret = platform_device_add_data(fme_region->region, &region_pdata,
> + sizeof(region_pdata));
> + if (ret)
> + goto create_region_err;
> +
> + ret = platform_device_add(fme_region->region);
> + if (ret)
> + goto create_region_err;
> +
> + fme_region->port_id = port_id;
> +
> + return fme_region;
> +
> +create_region_err:
> + platform_device_put(fme_region->region);
> + return ERR_PTR(ret);
> +}
> +
> +/**
> + * dfl_fme_destroy_region - destroy fme region
> + * @fme_region: fme region to destroy
> + */
> +static void dfl_fme_destroy_region(struct dfl_fme_region *fme_region)
> +{
> + platform_device_unregister(fme_region->region);
> +}
> +
> +/**
> + * dfl_fme_destroy_regions - destroy all fme regions
> + * @pdata: fme platform device's pdata
> + */
> +static void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata)
> +{
> + struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata);
> + struct dfl_fme_region *fme_region, *tmp;
> +
> + list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) {
> + list_del(&fme_region->node);
> + dfl_fme_destroy_region(fme_region);
> + }
> +}
> +
> +static int
> +pr_mgmt_init(struct platform_device *pdev, struct dfl_feature *feature)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> + struct dfl_fme_region *fme_region;
> + struct dfl_fme_bridge *fme_br;
> + struct platform_device *mgr;
> + struct dfl_fme *priv;
> + void __iomem *fme_hdr;
> + int ret = -ENODEV, i = 0;
> + u64 fme_cap, port_offset;
> +
> + fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev,
> + FME_FEATURE_ID_HEADER);
> +
> + mutex_lock(&pdata->lock);
> + priv = dfl_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 = dfl_fme_create_mgr(pdata, feature);
> + if (IS_ERR(mgr)) {
> + dev_err(&pdev->dev, "fail to create fpga mgr pdev\n");
> + goto unlock;
> + }
> +
> + 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 = dfl_fme_create_bridge(pdata, i);
> + if (IS_ERR(fme_br)) {
> + ret = PTR_ERR(fme_br);
> + goto destroy_region;
> + }
> +
> + list_add(&fme_br->node, &priv->bridge_list);
> +
> + /* Create region for each port */
> + fme_region = dfl_fme_create_region(pdata, mgr,
> + fme_br->br, i);
> + if (!fme_region) {
> + ret = PTR_ERR(fme_region);
> + goto destroy_region;
> + }
> +
> + list_add(&fme_region->node, &priv->region_list);
> + }
> + mutex_unlock(&pdata->lock);
> +
> + return 0;
> +
> +destroy_region:
> + dfl_fme_destroy_regions(pdata);
> + dfl_fme_destroy_bridges(pdata);
> + dfl_fme_destroy_mgr(pdata);
> +unlock:
> + mutex_unlock(&pdata->lock);
> + return ret;
> +}
> +
> +static void pr_mgmt_uinit(struct platform_device *pdev,
> + struct dfl_feature *feature)
> +{
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> + struct dfl_fme *priv;
> +
> + mutex_lock(&pdata->lock);
> + priv = dfl_fpga_pdata_get_private(pdata);
> +
> + dfl_fme_destroy_regions(pdata);
> + dfl_fme_destroy_bridges(pdata);
> + dfl_fme_destroy_mgr(pdata);
> + mutex_unlock(&pdata->lock);
> +}
> +
> +static long fme_pr_ioctl(struct platform_device *pdev,
> + struct dfl_feature *feature,
> + unsigned int cmd, unsigned long arg)
> +{
> + long ret;
> +
> + switch (cmd) {
> + case DFL_FPGA_FME_PORT_PR:
> + ret = fme_pr(pdev, arg);
> + break;
> + default:
> + ret = -ENODEV;
> + }
> +
> + return ret;
> +}
> +
> +const struct dfl_feature_ops pr_mgmt_ops = {
> + .init = pr_mgmt_init,
> + .uinit = pr_mgmt_uinit,
> + .ioctl = fme_pr_ioctl,
> +};
> diff --git a/drivers/fpga/dfl-fme-pr.h b/drivers/fpga/dfl-fme-pr.h
> new file mode 100644
> index 0000000..2e60b53
> --- /dev/null
> +++ b/drivers/fpga/dfl-fme-pr.h
> @@ -0,0 +1,84 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for FPGA Management Engine (FME) Partial Reconfiguration Driver
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + * Kang Luwei <[email protected]>
> + * Xiao Guangrong <[email protected]>
> + * Wu Hao <[email protected]>
> + * Joseph Grecco <[email protected]>
> + * Enno Luebbers <[email protected]>
> + * Tim Whisonant <[email protected]>
> + * Ananda Ravuri <[email protected]>
> + * Henry Mitchel <[email protected]>
> + */
> +
> +#ifndef __DFL_FME_PR_H
> +#define __DFL_FME_PR_H
> +
> +#include <linux/platform_device.h>
> +
> +/**
> + * struct dfl_fme_region - FME fpga region data structure
> + *
> + * @region: platform device of the FPGA region.
> + * @node: used to link fme_region to a list.
> + * @port_id: indicate which port this region connected to.
> + */
> +struct dfl_fme_region {
> + struct platform_device *region;
> + struct list_head node;
> + int port_id;
> +};
> +
> +/**
> + * struct dfl_fme_region_pdata - platform data for FME region platform device.
> + *
> + * @mgr: platform device of the FPGA manager.
> + * @br: platform device of the FPGA bridge.
> + * @region_id: region id (same as port_id).
> + */
> +struct dfl_fme_region_pdata {
> + struct platform_device *mgr;
> + struct platform_device *br;
> + int region_id;
> +};
> +
> +/**
> + * struct dfl_fme_bridge - FME fpga bridge data structure
> + *
> + * @br: platform device of the FPGA bridge.
> + * @node: used to link fme_bridge to a list.
> + */
> +struct dfl_fme_bridge {
> + struct platform_device *br;
> + struct list_head node;
> +};
> +
> +/**
> + * struct dfl_fme_bridge_pdata - platform data for FME bridge platform device.
> + *
> + * @cdev: container device.
> + * @port_id: port id.
> + */
> +struct dfl_fme_br_pdata {
> + struct dfl_fpga_cdev *cdev;
> + int port_id;
> +};
> +
> +/**
> + * struct dfl_fme_mgr_pdata - platform data for FME manager platform device.
> + *
> + * @ioaddr: mapped io address for FME manager platform device.
> + */
> +struct dfl_fme_mgr_pdata {
> + void __iomem *ioaddr;
> +};
> +
> +#define DFL_FPGA_FME_MGR "dfl-fme-mgr"
> +#define DFL_FPGA_FME_BRIDGE "dfl-fme-bridge"
> +#define DFL_FPGA_FME_REGION "dfl-fme-region"
> +
> +#endif /* __DFL_FME_PR_H */
> diff --git a/drivers/fpga/dfl-fme.h b/drivers/fpga/dfl-fme.h
> new file mode 100644
> index 0000000..73b864d
> --- /dev/null
> +++ b/drivers/fpga/dfl-fme.h
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for FPGA Management Engine (FME) Driver
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + * Kang Luwei <[email protected]>
> + * Xiao Guangrong <[email protected]>
> + * Wu Hao <[email protected]>
> + * Joseph Grecco <[email protected]>
> + * Enno Luebbers <[email protected]>
> + * Tim Whisonant <[email protected]>
> + * Ananda Ravuri <[email protected]>
> + * Henry Mitchel <[email protected]>
> + */
> +
> +#ifndef __DFL_FME_H
> +#define __DFL_FME_H
> +
> +/**
> + * struct dfl_fme - dfl fme private data
> + *
> + * @mgr: FME's FPGA manager platform device.
> + * @region_list: link list of FME's FPGA regions.
> + * @bridge_list: link list of FME's FPGA bridges.
> + * @pdata: fme platform device's pdata.
> + */
> +struct dfl_fme {
> + struct platform_device *mgr;
> + struct list_head region_list;
> + struct list_head bridge_list;
> + struct dfl_feature_platform_data *pdata;
> +};
> +
> +extern const struct dfl_feature_ops pr_mgmt_ops;
> +
> +#endif /* __DFL_FME_H */
> diff --git a/include/uapi/linux/fpga-dfl.h b/include/uapi/linux/fpga-dfl.h
> index 7585de0..1c8bb2d 100644
> --- a/include/uapi/linux/fpga-dfl.h
> +++ b/include/uapi/linux/fpga-dfl.h
> @@ -14,6 +14,8 @@
> #ifndef _UAPI_LINUX_FPGA_DFL_H
> #define _UAPI_LINUX_FPGA_DFL_H
>
> +#include <linux/types.h>
> +
> #define DFL_FPGA_API_VERSION 0
>
> /*
> @@ -26,6 +28,7 @@
> #define DFL_FPGA_MAGIC 0xB6
>
> #define DFL_FPGA_BASE 0
> +#define DFL_FME_BASE 0x80
>
> /**
> * DFL_FPGA_GET_API_VERSION - _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 0)
> @@ -45,4 +48,29 @@
>
> #define DFL_FPGA_CHECK_EXTENSION _IO(DFL_FPGA_MAGIC, DFL_FPGA_BASE + 1)
>
> +/* IOCTLs for FME file descriptor */
> +
> +/**
> + * DFL_FPGA_FME_PORT_PR - _IOW(DFL_FPGA_MAGIC, DFL_FME_BASE + 0,
> + * struct dfl_fpga_fme_port_pr)
> + *
> + * Driver does Partial Reconfiguration based on Port ID and Buffer (Image)
> + * provided by caller.
> + * Return: 0 on success, -errno on failure.
> + * If DFL_FPGA_FME_PORT_PR returns -EIO, that indicates the HW has detected
> + * some errors during PR, under this case, the user can fetch HW error info
> + * from the status of FME's fpga manager.
> + */
> +
> +struct dfl_fpga_fme_port_pr {
> + /* Input */
> + __u32 argsz; /* Structure length */
> + __u32 flags; /* Zero for now */
> + __u32 port_id;
> + __u32 buffer_size;
> + __u64 buffer_address; /* Userspace address to the buffer for PR */
> +};
> +
> +#define DFL_FPGA_FME_PORT_PR _IO(DFL_FPGA_MAGIC, DFL_FME_BASE + 0)
> +
> #endif /* _UAPI_LINUX_FPGA_DFL_H */
> --
> 1.8.3.1
>

2018-06-06 16:16:48

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 28/28] fpga: dfl: afu: add DFL_FPGA_PORT_DMA_MAP/UNMAP ioctls support

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> 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 DFL_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:
> * DFL_FPGA_PORT_DMA_MAP
> Do the dma mapping per user_addr and length which provided by user.
> Return iova in provided struct dfl_fpga_port_dma_map.
>
> * DFL_FPGA_PORT_DMA_UNMAP
> Unmap the dma region per iova provided by user.
>
> Signed-off-by: Tim Whisonant <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>

Acked-by: Alan Tull <[email protected]>

2018-06-06 16:19:06

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 14/28] fpga: dfl: add FPGA Management Engine driver basic framework

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

> From: Kang Luwei <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Shiva Rao <[email protected]>
> Signed-off-by: Christopher Rauer <[email protected]>
> Signed-off-by: Kang Luwei <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

> ---
> v3: rename driver from intel-fpga-fme to dfl-fme
> rename Kconfig from INTEL_FPGA_FME to FPGA_DFL_FME
> v4: fix SPDX license issue, use dfl-fme as module name
> v5: rebase, due to DFL framework naming changes on functions and data structures.
> fix uinit order in remove function.
> remove else block in fme_ioctl function per suggestion from Alan.
> ---
> drivers/fpga/Kconfig | 10 +++
> drivers/fpga/Makefile | 3 +
> drivers/fpga/dfl-fme-main.c | 158 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 171 insertions(+)
> create mode 100644 drivers/fpga/dfl-fme-main.c
>
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 87f3d44..103d5e2 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -140,6 +140,16 @@ config FPGA_DFL
> Gate Array (FPGA) solutions which implement Device Feature List.
> It provides enumeration APIs, and feature device infrastructure.
>
> +config FPGA_DFL_FME
> + tristate "FPGA DFL FME Driver"
> + depends on FPGA_DFL
> + help
> + The FPGA Management Engine (FME) is a feature device implemented
> + under Device Feature List (DFL) framework. Select this option to
> + enable the platform device driver for FME which implements all
> + FPGA platform level management features. There shall be 1 FME
> + per DFL based FPGA device.
> +
> config FPGA_DFL_PCI
> tristate "FPGA Device Feature List (DFL) PCIe Device Driver"
> depends on PCI && FPGA_DFL
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 4375630..fbd1c85 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -30,6 +30,9 @@ obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o
>
> # FPGA Device Feature List Support
> obj-$(CONFIG_FPGA_DFL) += dfl.o
> +obj-$(CONFIG_FPGA_DFL_FME) += dfl-fme.o
> +
> +dfl-fme-objs := dfl-fme-main.o
>
> # Drivers for FPGAs which implement DFL
> obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o
> diff --git a/drivers/fpga/dfl-fme-main.c b/drivers/fpga/dfl-fme-main.c
> new file mode 100644
> index 0000000..000651f
> --- /dev/null
> +++ b/drivers/fpga/dfl-fme-main.c
> @@ -0,0 +1,158 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Driver for FPGA Management Engine (FME)
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + * Kang Luwei <[email protected]>
> + * Xiao Guangrong <[email protected]>
> + * Joseph Grecco <[email protected]>
> + * Enno Luebbers <[email protected]>
> + * Tim Whisonant <[email protected]>
> + * Ananda Ravuri <[email protected]>
> + * Henry Mitchel <[email protected]>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include "dfl.h"
> +
> +static int
> +fme_hdr_init(struct platform_device *pdev, struct dfl_feature *feature)

Minor nit, where possible please stick with consistently having the
function name in the same line as the return type such as:

static int fme_hdr_init(struct platform_device *pdev, so on...

> +{
> + dev_dbg(&pdev->dev, "FME HDR Init.\n");
> +
> + return 0;
> +}
> +
> +static void
> +fme_hdr_uinit(struct platform_device *pdev, struct dfl_feature *feature)
> +{
> + dev_dbg(&pdev->dev, "FME HDR UInit.\n");
> +}
> +
> +static const struct dfl_feature_ops fme_hdr_ops = {
> + .init = fme_hdr_init,
> + .uinit = fme_hdr_uinit,
> +};
> +
> +static struct dfl_feature_driver fme_feature_drvs[] = {
> + {
> + .id = FME_FEATURE_ID_HEADER,
> + .ops = &fme_hdr_ops,
> + },
> + {
> + .ops = NULL,
> + },
> +};
> +
> +static int fme_open(struct inode *inode, struct file *filp)
> +{
> + struct platform_device *fdev = dfl_fpga_inode_to_feature_dev(inode);
> + struct dfl_feature_platform_data *pdata = dev_get_platdata(&fdev->dev);
> + int ret;
> +
> + if (WARN_ON(!pdata))
> + return -ENODEV;
> +
> + ret = dfl_feature_dev_use_begin(pdata);
> + if (ret)
> + return ret;
> +
> + dev_dbg(&fdev->dev, "Device File Open\n");
> + filp->private_data = pdata;
> +
> + return 0;
> +}
> +
> +static int fme_release(struct inode *inode, struct file *filp)
> +{
> + struct dfl_feature_platform_data *pdata = filp->private_data;
> + struct platform_device *pdev = pdata->dev;
> +
> + dev_dbg(&pdev->dev, "Device File Release\n");
> + dfl_feature_dev_use_end(pdata);
> +
> + return 0;
> +}
> +
> +static long fme_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> +{
> + struct dfl_feature_platform_data *pdata = filp->private_data;
> + struct platform_device *pdev = pdata->dev;
> + struct dfl_feature *f;
> + long ret;
> +
> + dev_dbg(&pdev->dev, "%s cmd 0x%x\n", __func__, cmd);
> +
> + switch (cmd) {
> + default:
> + /*
> + * Let sub-feature's ioctl function to handle the cmd
> + * Sub-feature's ioctl returns -ENODEV when cmd is not
> + * handled in this sub feature, and returns 0 and other
> + * error code if cmd is handled.
> + */
> + dfl_fpga_dev_for_each_feature(pdata, f) {
> + if (f->ops && f->ops->ioctl) {
> + ret = f->ops->ioctl(pdev, f, cmd, arg);
> + if (ret != -ENODEV)
> + return ret;
> + }
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static const struct file_operations fme_fops = {
> + .owner = THIS_MODULE,
> + .open = fme_open,
> + .release = fme_release,
> + .unlocked_ioctl = fme_ioctl,
> +};
> +
> +static int fme_probe(struct platform_device *pdev)
> +{
> + int ret;
> +
> + ret = dfl_fpga_dev_feature_init(pdev, fme_feature_drvs);
> + if (ret)
> + goto exit;
> +
> + ret = dfl_fpga_register_dev_ops(pdev, &fme_fops, THIS_MODULE);
> + if (ret)
> + goto feature_uinit;
> +
> + return 0;
> +
> +feature_uinit:
> + dfl_fpga_dev_feature_uinit(pdev);
> +exit:
> + return ret;
> +}
> +
> +static int fme_remove(struct platform_device *pdev)
> +{
> + dfl_fpga_unregister_dev_ops(pdev);
> + dfl_fpga_dev_feature_uinit(pdev);
> +
> + return 0;
> +}
> +
> +static struct platform_driver fme_driver = {
> + .driver = {
> + .name = DFL_FPGA_FEATURE_DEV_FME,
> + },
> + .probe = fme_probe,
> + .remove = fme_remove,
> +};
> +
> +module_platform_driver(fme_driver);
> +
> +MODULE_DESCRIPTION("FPGA Management Engine driver");
> +MODULE_AUTHOR("Intel Corporation");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dfl-fme");
> --
> 1.8.3.1
>

2018-06-06 16:19:16

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 01/28] docs: fpga: add a document for FPGA Device Feature List (DFL) Framework Overview

On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:

Hi Hao,

I've acked the remaining patches with some changes requested. In your
v6, please add a patch to add yourself to the MAINTAINERS file or
whoever is planning on maintaining fpga/drivers/*dfl*

> Add a document for FPGA Device Feature List (DFL) Framework Overview.
>
> Signed-off-by: Enno Luebbers <[email protected]>
> Signed-off-by: Xiao Guangrong <[email protected]>
> Signed-off-by: Wu Hao <[email protected]>
Acked-by: Alan Tull <[email protected]>

Thanks,
Alan

2018-06-07 02:48:21

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 01/28] docs: fpga: add a document for FPGA Device Feature List (DFL) Framework Overview

On Wed, Jun 06, 2018 at 11:16:03AM -0500, Alan Tull wrote:
> On Tue, May 1, 2018 at 9:50 PM, Wu Hao <[email protected]> wrote:
>
> Hi Hao,
>
> I've acked the remaining patches with some changes requested. In your
> v6, please add a patch to add yourself to the MAINTAINERS file or
> whoever is planning on maintaining fpga/drivers/*dfl*

Hi Alan,

Sure, I will fix these in the next version. Thanks a lot for the review.

Hao

>
> > Add a document for FPGA Device Feature List (DFL) Framework Overview.
> >
> > Signed-off-by: Enno Luebbers <[email protected]>
> > Signed-off-by: Xiao Guangrong <[email protected]>
> > Signed-off-by: Wu Hao <[email protected]>
> Acked-by: Alan Tull <[email protected]>
>
> Thanks,
> Alan

2018-06-07 19:08:29

by Alan Tull

[permalink] [raw]
Subject: Re: [PATCH v5 07/28] fpga: dfl: add chardev support for feature devices

On Wed, Jun 6, 2018 at 7:24 AM, Wu Hao <[email protected]> wrote:

Hi Hao,

One more...

>> > +static dev_t dfl_get_devt(enum dfl_fpga_devt_type type, int id)
>> > +{
>> > + WARN_ON(type >= DFL_FPGA_DEVT_MAX);
>> > +
>> > + return MKDEV(MAJOR(dfl_chrdevs[type].devt), id);
>> > +}
>> > +
>> > +/**
>> > + * dfl_fpga_register_dev_ops - register cdev ops for feature dev
>> > + *
>> > + * @pdev: feature dev.
>> > + * @fops: file operations for feature dev's cdev.
>> > + * @owner: owning module/driver.
>> > + *
>> > + * Return: 0 on success, negative error code otherwise.
>> > + */
>> > +int dfl_fpga_register_dev_ops(struct platform_device *pdev,
>> > + const struct file_operations *fops,
>> > + struct module *owner)
>> > +{
>> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
>> > +
>> > + cdev_init(&pdata->cdev, fops);
>> > + pdata->cdev.owner = owner;
>> > +
>> > + /*
>> > + * set parent to the feature device so that its refcount is
>> > + * decreased after the last refcount of cdev is gone, that
>> > + * makes sure the feature device is valid during device
>> > + * file's life-cycle.
>> > + */
>> > + pdata->cdev.kobj.parent = &pdev->dev.kobj;
>> > +
>> > + return cdev_add(&pdata->cdev, pdev->dev.devt, 1);
>> > +}
>> > +EXPORT_SYMBOL_GPL(dfl_fpga_register_dev_ops);
>> > +
>> > +/**
>> > + * dfl_fpga_unregister_dev_ops - unregister cdev ops for feature dev
>> > + * @pdev: feature dev.
>> > + */
>> > +void dfl_fpga_unregister_dev_ops(struct platform_device *pdev)
>> > +{
>> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
>> > +
>> > + cdev_del(&pdata->cdev);
>> > +}
>> > +EXPORT_SYMBOL_GPL(dfl_fpga_unregister_dev_ops);

How about dfl_fpga_dev_ops_register/unregister?

Thanks,
Alan

2018-06-08 00:22:52

by Wu Hao

[permalink] [raw]
Subject: Re: [PATCH v5 07/28] fpga: dfl: add chardev support for feature devices

On Thu, Jun 07, 2018 at 01:03:18PM -0500, Alan Tull wrote:
> On Wed, Jun 6, 2018 at 7:24 AM, Wu Hao <[email protected]> wrote:
>
> Hi Hao,
>
> One more...
>
> >> > +static dev_t dfl_get_devt(enum dfl_fpga_devt_type type, int id)
> >> > +{
> >> > + WARN_ON(type >= DFL_FPGA_DEVT_MAX);
> >> > +
> >> > + return MKDEV(MAJOR(dfl_chrdevs[type].devt), id);
> >> > +}
> >> > +
> >> > +/**
> >> > + * dfl_fpga_register_dev_ops - register cdev ops for feature dev
> >> > + *
> >> > + * @pdev: feature dev.
> >> > + * @fops: file operations for feature dev's cdev.
> >> > + * @owner: owning module/driver.
> >> > + *
> >> > + * Return: 0 on success, negative error code otherwise.
> >> > + */
> >> > +int dfl_fpga_register_dev_ops(struct platform_device *pdev,
> >> > + const struct file_operations *fops,
> >> > + struct module *owner)
> >> > +{
> >> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> >> > +
> >> > + cdev_init(&pdata->cdev, fops);
> >> > + pdata->cdev.owner = owner;
> >> > +
> >> > + /*
> >> > + * set parent to the feature device so that its refcount is
> >> > + * decreased after the last refcount of cdev is gone, that
> >> > + * makes sure the feature device is valid during device
> >> > + * file's life-cycle.
> >> > + */
> >> > + pdata->cdev.kobj.parent = &pdev->dev.kobj;
> >> > +
> >> > + return cdev_add(&pdata->cdev, pdev->dev.devt, 1);
> >> > +}
> >> > +EXPORT_SYMBOL_GPL(dfl_fpga_register_dev_ops);
> >> > +
> >> > +/**
> >> > + * dfl_fpga_unregister_dev_ops - unregister cdev ops for feature dev
> >> > + * @pdev: feature dev.
> >> > + */
> >> > +void dfl_fpga_unregister_dev_ops(struct platform_device *pdev)
> >> > +{
> >> > + struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> >> > +
> >> > + cdev_del(&pdata->cdev);
> >> > +}
> >> > +EXPORT_SYMBOL_GPL(dfl_fpga_unregister_dev_ops);
>
> How about dfl_fpga_dev_ops_register/unregister?

Sure, will fix this in the v6. Thanks.

Hao

>
> Thanks,
> Alan
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fpga" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html