lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 28 Nov 2017 22:07:36 -0800
From:   Moritz Fischer <mdf@...nel.org>
To:     Wu Hao <hao.wu@...el.com>
Cc:     atull@...nel.org, mdf@...nel.org, linux-fpga@...r.kernel.org,
        linux-kernel@...r.kernel.org, linux-api@...r.kernel.org,
        luwei.kang@...el.com, yi.z.zhang@...el.com,
        Tim Whisonant <tim.whisonant@...el.com>,
        Enno Luebbers <enno.luebbers@...el.com>,
        Shiva Rao <shiva.rao@...el.com>,
        Christopher Rauer <christopher.rauer@...el.com>,
        Xiao Guangrong <guangrong.xiao@...ux.intel.com>
Subject: Re: [PATCH v3 04/21] fpga: add device feature list support

Hi Hao,

first pass, I didn't get all the way through, yet.

On Mon, Nov 27, 2017 at 02:42:11PM +0800, Wu Hao wrote:
> Device Feature List (DFL) defines a feature list structure that creates
> a link list of feature headers within the MMIO space to provide an
> extensible way of adding features. This patch introduces a kernel module
> to provide basic infrastructure to support FPGA devices which implement
> the Device Feature List.
> 
> Usually there will be different features and their sub features linked into
> the DFL. This code provides common APIs for feature enumeration, it creates
> a container device (FPGA base region), walks through the DFLs and creates
> platform devices for feature devices (Currently it only supports two
> different feature devices, FPGA Management Engine (FME) and Port which
> the Accelerator Function Unit (AFU) connected to). In order to enumerate
> the DFLs, the common APIs required low level driver to provide necessary
> enumeration information (e.g address for each device feature list for
> given device) and fill it to the fpga_enum_info data structure. Please
> refer to below description for APIs added for enumeration.
> 
> Functions for enumeration information preparation:
>  *fpga_enum_info_alloc
>    allocate enumeration information data structure.
> 
>  *fpga_enum_info_add_dfl
>    add a device feature list to fpga_enum_info data structure.
> 
>  *fpga_enum_info_free
>    free fpga_enum_info data structure and related resources.
> 
> Functions for feature device enumeration:
>  *fpga_enumerate_feature_devs
>    enumerate feature devices and return container device.
> 
>  *fpga_remove_feature_devs
>    remove feature devices under given container device.
> 
> Signed-off-by: Tim Whisonant <tim.whisonant@...el.com>
> Signed-off-by: Enno Luebbers <enno.luebbers@...el.com>
> Signed-off-by: Shiva Rao <shiva.rao@...el.com>
> Signed-off-by: Christopher Rauer <christopher.rauer@...el.com>
> Signed-off-by: Zhang Yi <yi.z.zhang@...el.com>
> Signed-off-by: Xiao Guangrong <guangrong.xiao@...ux.intel.com>
> Signed-off-by: Wu Hao <hao.wu@...el.com>
> ----
> v3: split from another patch.
>     separate dfl enumeration code from original pcie driver.
>     provide common data structures and APIs for enumeration.
>     update device feature list parsing process according to latest hw.
>     add dperf/iperf/hssi sub feature placeholder according to latest hw.
>     remove build_info_add_sub_feature and other small functions.
>     replace *_feature_num function with macro.
>     remove writeq/readq.
> ---
>  drivers/fpga/Kconfig    |  16 +
>  drivers/fpga/Makefile   |   3 +
>  drivers/fpga/fpga-dfl.c | 884 ++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/fpga/fpga-dfl.h | 365 ++++++++++++++++++++
>  4 files changed, 1268 insertions(+)
>  create mode 100644 drivers/fpga/fpga-dfl.c
>  create mode 100644 drivers/fpga/fpga-dfl.h
> 
> 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..447ba2b 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)			+= fpga-dfl.o
> diff --git a/drivers/fpga/fpga-dfl.c b/drivers/fpga/fpga-dfl.c
> new file mode 100644
> index 0000000..6609828
> --- /dev/null
> +++ b/drivers/fpga/fpga-dfl.c
> @@ -0,0 +1,884 @@
> +/*
> + * Driver for FPGA Device Feature List (DFL) Support
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + *   Kang Luwei <luwei.kang@...el.com>
> + *   Zhang Yi <yi.z.zhang@...el.com>
> + *   Wu Hao <hao.wu@...el.com>
> + *   Xiao Guangrong <guangrong.xiao@...ux.intel.com>
> + *
> + * This work is licensed under the terms of the GNU GPL version 2.

This is redundant.
> + * SPDX-License-Identifier: GPL-2.0

Also I think the current consensus is that this should go in the first
line
> + */
> +#include <linux/module.h>
> +
> +#include "fpga-dfl.h"
> +
> +static DEFINE_MUTEX(fpga_id_mutex);
> +
> +enum fpga_id_type {
> +	FME_ID,		/* fme id allocation and mapping */
> +	PORT_ID,	/* port id allocation and mapping */
> +	FPGA_ID_MAX,
> +};
> +
> +/* it is protected by fpga_id_mutex */
> +static struct idr fpga_ids[FPGA_ID_MAX];
> +
> +static void fpga_ids_init(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fpga_ids); i++)
> +		idr_init(fpga_ids + i);
> +}
> +
> +static void fpga_ids_destroy(void)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(fpga_ids); i++)
> +		idr_destroy(fpga_ids + i);
> +}
> +
> +static int alloc_fpga_id(enum fpga_id_type type, struct device *dev)
> +{
> +	int id;
> +
> +	WARN_ON(type >= FPGA_ID_MAX);
> +	mutex_lock(&fpga_id_mutex);
> +	id = idr_alloc(fpga_ids + type, dev, 0, 0, GFP_KERNEL);
> +	mutex_unlock(&fpga_id_mutex);
> +
> +	return id;
> +}
> +
> +static void free_fpga_id(enum fpga_id_type type, int id)
> +{
> +	WARN_ON(type >= FPGA_ID_MAX);
> +	mutex_lock(&fpga_id_mutex);
> +	idr_remove(fpga_ids + type, id);
> +	mutex_unlock(&fpga_id_mutex);
> +}
> +
> +static enum fpga_id_type feature_dev_id_type(struct platform_device *pdev)
> +{
> +	if (!strcmp(pdev->name, FPGA_FEATURE_DEV_FME))
> +		return FME_ID;
> +
> +	if (!strcmp(pdev->name, FPGA_FEATURE_DEV_PORT))
> +		return PORT_ID;
> +
> +	WARN_ON(1);

Do we really need a WARN_ON() here? Wouldn't returning an error be
nicer?
> +
> +	return FPGA_ID_MAX;
> +}
> +
> +/**
> + * build_feature_devs_info - info collected during feature dev build.
> + *
> + * @dev: device to enumerate.
> + * @cdev: the container device for all feature devices.
> + * @feature_dev: current feature device.
> + */
> +struct build_feature_devs_info {
> +	struct device *dev;
> +	struct fpga_cdev *cdev;
> +	struct platform_device *feature_dev;
> +};
> +
> +static void fpga_cdev_add_port_dev(struct fpga_cdev *cdev,
> +				   struct platform_device *port_pdev)
> +{
> +	struct feature_platform_data *pdata = dev_get_platdata(&port_pdev->dev);
> +
> +	mutex_lock(&cdev->lock);
> +	list_add(&pdata->node, &cdev->port_dev_list);
> +	get_device(&pdata->dev->dev);
> +	mutex_unlock(&cdev->lock);
> +}
> +
> +/*
> + * register current feature device, it is called when we need to switch to
> + * another feature parsing or we have parsed all features on given device
> + * feature list.
> + */
> +static int build_info_commit_dev(struct build_feature_devs_info *binfo)
> +{
> +	int ret;
> +
> +	if (!binfo->feature_dev)
> +		return 0;
> +
> +	ret = platform_device_add(binfo->feature_dev);
> +	if (!ret) {
> +		if (feature_dev_id_type(binfo->feature_dev) == PORT_ID)
> +			fpga_cdev_add_port_dev(binfo->cdev, binfo->feature_dev);
> +		else

So if you get back FPGA_ID_MAX, it is automatically a fme_dev?
> +			binfo->cdev->fme_dev =
> +					get_device(&binfo->feature_dev->dev);
> +		/*
> +		 * reset it to avoid build_info_free() freeing their resource.
> +		 *
> +		 * The resource of successfully registered feature devices
> +		 * will be freed by platform_device_unregister(). See the
> +		 * comments in build_info_create_dev().
> +		 */
> +		binfo->feature_dev = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int
> +build_info_create_dev(struct build_feature_devs_info *binfo,
> +		      enum fpga_id_type type, int feature_nr, const char *name)
> +{
> +	struct platform_device *fdev;
> +	struct resource *res;
> +	struct feature_platform_data *pdata;
> +	int ret;
> +
> +	/* we will create a new device, commit current device first */
> +	ret = build_info_commit_dev(binfo);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * we use -ENODEV as the initialization indicator which indicates
> +	 * whether the id need to be reclaimed
> +	 */
> +	fdev = platform_device_alloc(name, -ENODEV);
> +	if (!fdev)
> +		return -ENOMEM;
> +
> +	binfo->feature_dev = fdev;
> +
> +	fdev->id = alloc_fpga_id(type, &fdev->dev);
> +	if (fdev->id < 0)
> +		return fdev->id;
> +
> +	fdev->dev.parent = &binfo->cdev->region.dev;
> +
> +	/*
> +	 * we do not need to care for the memory which is associated with
> +	 * the platform device. After calling platform_device_unregister(),
> +	 * it will be automatically freed by device's release() callback,
> +	 * platform_device_release().
> +	 */
> +	pdata = kzalloc(feature_platform_data_size(feature_nr), GFP_KERNEL);
> +	if (pdata) {
> +		pdata->dev = fdev;
> +		pdata->num = feature_nr;
> +		mutex_init(&pdata->lock);
> +	} else {
> +		return -ENOMEM;
Does this path clean up fdev->id? Does that happen in
platform_device_release() ?
> +	}
> +
> +	/*
> +	 * the count should be initialized to 0 to make sure
> +	 *__fpga_port_enable() following __fpga_port_disable()
> +	 * works properly for port device.
> +	 * and it should always be 0 for fme device.
> +	 */
> +	WARN_ON(pdata->disable_count);
> +
> +	fdev->dev.platform_data = pdata;
> +	fdev->num_resources = feature_nr;
> +	fdev->resource = kcalloc(feature_nr, sizeof(*res), GFP_KERNEL);
> +	if (!fdev->resource)
> +		return -ENOMEM;
> +
> +	return 0;
> +}
> +
> +static void build_info_free(struct build_feature_devs_info *binfo)
> +{
> +	/*
> +	 * it is a valid id, free it. See comments in
> +	 * build_info_create_dev()
> +	 */
> +	if (binfo->feature_dev && binfo->feature_dev->id >= 0)
> +		free_fpga_id(feature_dev_id_type(binfo->feature_dev),
> +			     binfo->feature_dev->id);
> +
> +	platform_device_put(binfo->feature_dev);
> +
> +	devm_kfree(binfo->dev, binfo);
> +}
> +
> +/*
> + * UAFU GUID is dynamic as it can be changed after FME downloads different
> + * Green Bitstream to the port, so we treat the unknown GUIDs which are
> + * attached on port's feature list as UAFU.
> + */
> +static bool feature_is_UAFU(struct build_feature_devs_info *binfo)
> +{
> +	if (!binfo->feature_dev ||
> +	    feature_dev_id_type(binfo->feature_dev) != PORT_ID)
> +		return false;
> +
> +	return true;
> +}
> +
> +struct feature_info {
> +	const char *name;
> +	resource_size_t resource_size;
> +	int feature_index;
> +};
> +
> +/* indexed by fme feature IDs which are defined in 'enum fme_feature_id'. */
> +static struct feature_info fme_features[] = {
> +	{
> +		.name = FME_FEATURE_HEADER,
> +		.resource_size = FME_HDR_SIZE,
> +		.feature_index = FME_FEATURE_ID_HEADER,
> +	},
> +	{
> +		.name = FME_FEATURE_THERMAL_MGMT,
> +		.resource_size = FME_THERMAL_SIZE,
> +		.feature_index = FME_FEATURE_ID_THERMAL_MGMT,
> +	},
> +	{
> +		.name = FME_FEATURE_POWER_MGMT,
> +		.resource_size = FME_POWER_SIZE,
> +		.feature_index = FME_FEATURE_ID_POWER_MGMT,
> +	},
> +	{
> +		.name = FME_FEATURE_GLOBAL_IPERF,
> +		.resource_size = FME_IPERF_SIZE,
> +		.feature_index = FME_FEATURE_ID_GLOBAL_IPERF,
> +	},
> +	{
> +		.name = FME_FEATURE_GLOBAL_ERR,
> +		.resource_size = FME_ERR_SIZE,
> +		.feature_index = FME_FEATURE_ID_GLOBAL_ERR,
> +	},
> +	{
> +		.name = FME_FEATURE_PR_MGMT,
> +		.resource_size = FME_PR_SIZE,
> +		.feature_index = FME_FEATURE_ID_PR_MGMT,
> +	},
> +	{
> +		.name = FME_FEATURE_HSSI,
> +		.resource_size = FME_HSSI_SIZE,
> +		.feature_index = FME_FEATURE_ID_HSSI,
> +	},
> +	{
> +		.name = FME_FEATURE_GLOBAL_DPERF,
> +		.resource_size = FME_DPERF_SIZE,
> +		.feature_index = FME_FEATURE_ID_GLOBAL_DPERF,
> +	},
> +};
> +
> +/* indexed by port feature IDs which are defined in 'enum port_feature_id'. */
> +static struct feature_info port_features[] = {
> +	{
> +		.name = PORT_FEATURE_HEADER,
> +		.resource_size = PORT_HDR_SIZE,
> +		.feature_index = PORT_FEATURE_ID_HEADER,
> +	},
> +	{
> +		.name = PORT_FEATURE_ERR,
> +		.resource_size = PORT_ERR_SIZE,
> +		.feature_index = PORT_FEATURE_ID_ERROR,
> +	},
> +	{
> +		.name = PORT_FEATURE_UMSG,
> +		.resource_size = PORT_UMSG_SIZE,
> +		.feature_index = PORT_FEATURE_ID_UMSG,
> +	},
> +	{
> +		/* This feature isn't available for now */
> +		.name = PORT_FEATURE_PR,
> +		.resource_size = DFH_SIZE,
> +		.feature_index = PORT_FEATURE_ID_PR,
> +	},
> +	{
> +		.name = PORT_FEATURE_STP,
> +		.resource_size = PORT_STP_SIZE,
> +		.feature_index = PORT_FEATURE_ID_STP,
> +	},
> +	{
> +		/*
> +		 * For User AFU feature, its region size is not fixed, but
> +		 * reported by register PortCapability.mmio_size. Resource
> +		 * size of UAFU will be set while parse port device.
> +		 */
> +		.name = PORT_FEATURE_UAFU,
> +		.resource_size = 0,
> +		.feature_index = PORT_FEATURE_ID_UAFU,
> +	},
> +};
> +
> +static int
> +create_feature_instance(struct build_feature_devs_info *binfo,
> +			struct fpga_enum_dfl *dfl, resource_size_t ofst,
> +			struct feature_info *finfo)
> +{
> +	int index = finfo->feature_index;
> +	struct platform_device *fdev = binfo->feature_dev;
> +	struct feature_platform_data *pdata = dev_get_platdata(&fdev->dev);
> +	struct resource *res = &fdev->resource[index];
> +
> +	if ((dfl->len - ofst < finfo->resource_size) || pdata->num < index)
> +		return -EINVAL;
> +
> +	res->start = dfl->start + ofst;
> +	res->end = res->start + finfo->resource_size - 1;
> +	res->flags = IORESOURCE_MEM;
> +	res->name = finfo->name;
> +
> +	pdata->features[index].name = finfo->name;
> +	pdata->features[index].resource_index = index;
> +	pdata->features[index].ioaddr = dfl->ioaddr + ofst;
> +
> +	return 0;
> +}
> +
> +static int parse_feature_fme(struct build_feature_devs_info *binfo,
> +			     struct fpga_enum_dfl *dfl,
> +			     resource_size_t ofst)
> +{
> +	int ret;
> +
> +	ret = build_info_create_dev(binfo, FME_ID, FME_FEATURE_NUM,
> +				    FPGA_FEATURE_DEV_FME);
> +	if (ret)
> +		return ret;
> +
> +	return create_feature_instance(binfo, dfl, ofst,
> +				       &fme_features[FME_FEATURE_ID_HEADER]);
> +}
> +
> +static int parse_feature_fme_private(struct build_feature_devs_info *binfo,
> +				     struct fpga_enum_dfl *dfl,
> +				     resource_size_t ofst)
> +{
> +	u64 v;
> +	int id;
> +
> +	v = readq(dfl->ioaddr + ofst + DFH);
> +	id = FIELD_GET(DFH_ID, v);
> +
> +	if (id >= ARRAY_SIZE(fme_features)) {
> +		dev_info(binfo->dev, "FME feature id %x is not supported yet.\n",
> +			 id);
> +		return 0;
> +	}
> +
> +	return create_feature_instance(binfo, dfl, ofst, &fme_features[id]);
> +}
> +
> +static int parse_feature_port(struct build_feature_devs_info *binfo,
> +			      struct fpga_enum_dfl *dfl,
> +			      resource_size_t ofst)
> +{
> +	int ret;
> +
> +	ret = build_info_create_dev(binfo, PORT_ID, PORT_FEATURE_NUM,
> +				    FPGA_FEATURE_DEV_PORT);
> +	if (ret)
> +		return ret;
> +
> +	return create_feature_instance(binfo, dfl, ofst,
> +				       &port_features[PORT_FEATURE_ID_HEADER]);
> +}
> +
> +static void enable_port_uafu(struct build_feature_devs_info *binfo)
> +{
> +	enum port_feature_id id = PORT_FEATURE_ID_UAFU;
> +	void __iomem *base;
> +	u64 v;
> +
> +	base = get_feature_ioaddr_by_index(&binfo->feature_dev->dev,
> +					   PORT_FEATURE_ID_HEADER);
> +
> +	v = readq(base + PORT_HDR_CAP);
> +	port_features[id].resource_size =
> +			FIELD_GET(PORT_CAP_MMIO_SIZE, v) << 10;
> +
> +	/*
> +	 * To enable User AFU, driver needs to clear reset bit on related port,
> +	 * otherwise the mmio space of this user AFU will be invalid.
> +	 */
> +	if (port_features[id].resource_size)
> +		fpga_port_reset(binfo->feature_dev);
> +}
> +
> +static int parse_feature_port_private(struct build_feature_devs_info *binfo,
> +				      struct fpga_enum_dfl *dfl,
> +				      resource_size_t ofst)
> +{
> +	enum port_feature_id id;
> +	u32 dfh_id;
> +	u64 v;
> +
> +	v = readq(dfl->ioaddr + ofst + DFH);
> +	dfh_id = FIELD_GET(DFH_ID, v);
> +
> +	/*
> +	 * the region of port feature id is [0x10, 0x13], + 1 to reserve 0
> +	 * which is dedicated for port-hdr.
> +	 */
> +	id = (dfh_id & 0x000f) + 1;
> +
> +	if (id >= ARRAY_SIZE(port_features)) {
> +		dev_info(binfo->dev, "Port feature id %x is not supported yet.\n",
> +			 dfh_id);
> +		return 0;
> +	}
> +
> +	return create_feature_instance(binfo, dfl, ofst, &port_features[id]);
> +}
> +
> +static int parse_feature_port_uafu(struct build_feature_devs_info *binfo,
> +				   struct fpga_enum_dfl *dfl,
> +				   resource_size_t ofst)
> +{
> +	enum port_feature_id id = PORT_FEATURE_ID_UAFU;
> +	int ret;
> +
> +	if (port_features[id].resource_size) {
> +		ret = create_feature_instance(binfo, dfl, ofst,
> +					      &port_features[id]);
> +		port_features[id].resource_size = 0;
> +	} else {
> +		dev_err(binfo->dev, "the uafu feature header is mis-configured.\n");
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int parse_feature_afus(struct build_feature_devs_info *binfo,
> +			      struct fpga_enum_dfl *dfl,
> +			      resource_size_t ofst)
> +{
> +	void __iomem *start = dfl->ioaddr + ofst;
> +	void __iomem *end = dfl->ioaddr + dfl->len;
> +	u32 offset;
> +	u64 v;
> +	int ret;
> +
> +	for (; start < end; start += offset) {
> +		if (end - start < AFU_DFH_SIZE)
> +			return -EINVAL;
> +
> +		if (feature_is_UAFU(binfo))
> +			ret = parse_feature_port_uafu(binfo, dfl,
> +						      start - dfl->ioaddr);
> +			if (ret)
> +				return ret;
> +
> +		v = readq(start + NEXT_AFU);
> +
> +		offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v);
> +		if (!offset)
> +			break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_feature_fiu(struct build_feature_devs_info *binfo,
> +			     struct fpga_enum_dfl *dfl,
> +			     resource_size_t ofst)
> +{
> +	u32 id, offset;
> +	u64 v;
> +	int ret = 0;
> +
> +	v = readq(dfl->ioaddr + ofst + DFH);
> +	id = FIELD_GET(DFH_ID, v);
> +
> +	switch (id) {
> +	case DFH_ID_FIU_FME:
> +		return parse_feature_fme(binfo, dfl, ofst);
> +	case DFH_ID_FIU_PORT:
> +		ret = parse_feature_port(binfo, dfl, ofst);
> +		enable_port_uafu(binfo);
> +		if (ret)
> +			return ret;
> +
> +		/* Check Port FIU's next_afu pointer to User AFU DFH */
> +		v = readq(dfl->ioaddr + ofst + NEXT_AFU);
> +
> +		offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v);
> +		if (offset)
> +			return parse_feature_afus(binfo, dfl, ofst + offset);
> +
> +		dev_dbg(binfo->dev, "No AFUs detected on Port\n");
> +		break;
> +	default:
> +		dev_info(binfo->dev, "FIU TYPE %d is not supported yet.\n",
> +			 id);
> +	}
> +
> +	return ret;
> +}
> +
> +static int parse_feature_private(struct build_feature_devs_info *binfo,
> +				 struct fpga_enum_dfl *dfl,
> +				 resource_size_t ofst)
> +{
> +	u64 v;
> +	u32 id;
> +
> +	v = readq(dfl->ioaddr + ofst + DFH);
> +	id = FIELD_GET(DFH_ID, v);
> +
> +	if (!binfo->feature_dev) {
> +		dev_err(binfo->dev, "the private feature %x does not belong to any AFU.\n",
> +			id);
> +		return -EINVAL;
> +	}
> +
> +	switch (feature_dev_id_type(binfo->feature_dev)) {
> +	case FME_ID:
> +		return parse_feature_fme_private(binfo, dfl, ofst);
> +	case PORT_ID:
> +		return parse_feature_port_private(binfo, dfl, ofst);
> +	default:
> +		dev_info(binfo->dev, "private feature %x belonging to AFU %s is not supported yet.\n",
> +			 id, binfo->feature_dev->name);
> +	}
> +	return 0;
> +}
> +
> +/**
> + * parse_feature - parse a feature on given device feature list
> + *
> + * @binfo: build feature devices information.
> + * @dfl: device feature list to parse
> + * @ofst: offset to feature header on this device feature list
> + */
> +static int parse_feature(struct build_feature_devs_info *binfo,
> +			 struct fpga_enum_dfl *dfl, resource_size_t ofst)
> +{
> +	u64 v;
> +	u32 type;
> +
> +	v = readq(dfl->ioaddr + ofst + DFH);
> +	type = FIELD_GET(DFH_TYPE, v);
> +
> +	switch (type) {
> +	case DFH_TYPE_AFU:
> +		return parse_feature_afus(binfo, dfl, ofst);
> +	case DFH_TYPE_PRIVATE:
> +		return parse_feature_private(binfo, dfl, ofst);
> +	case DFH_TYPE_FIU:
> +		return parse_feature_fiu(binfo, dfl, ofst);
> +	default:
> +		dev_info(binfo->dev,
> +			 "Feature Type %x is not supported.\n", type);
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_feature_list(struct build_feature_devs_info *binfo,
> +			      struct fpga_enum_dfl *dfl)
> +{
> +	void __iomem *start = dfl->ioaddr;
> +	void __iomem *end = dfl->ioaddr + dfl->len;
> +	int ret = 0;
> +	u32 ofst = 0;
> +	u64 v;
> +
> +	/* walk through the device feature list via DFH's next DFH pointer. */
> +	for (; start < end; start += ofst) {
> +		if (end - start < DFH_SIZE) {
> +			dev_err(binfo->dev, "The region is too small to contain a feature.\n");
> +			return -EINVAL;
> +		}
> +
> +		ret = parse_feature(binfo, dfl, start - dfl->ioaddr);
> +		if (ret)
> +			return ret;
> +
> +		v = readq(start + DFH);
> +		ofst = FIELD_GET(DFH_NEXT_HDR_OFST, v);
> +
> +		/* stop parsing if EOL(End of List) is set or offset is 0 */
> +		if ((v & DFH_EOL) || !ofst)
> +			break;
> +	}
> +
> +	/* commit current feature device when reach the end of list */
> +	return build_info_commit_dev(binfo);
> +}
> +
> +struct fpga_enum_info *fpga_enum_info_alloc(struct device *dev)
> +{
> +	struct fpga_enum_info *info;
> +
> +	get_device(dev);
> +
> +	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
> +	if (!info) {
> +		put_device(dev);
> +		return NULL;
> +	}
> +
> +	info->dev = dev;
> +	INIT_LIST_HEAD(&info->dfls);
> +
> +	return info;
> +}
> +EXPORT_SYMBOL_GPL(fpga_enum_info_alloc);
> +
> +int fpga_enum_info_add_dfl(struct fpga_enum_info *info, resource_size_t start,
> +			   resource_size_t len, void __iomem *ioaddr)
> +{
> +	struct fpga_enum_dfl *dfl;
> +
> +	dfl = devm_kzalloc(info->dev, sizeof(*dfl), GFP_KERNEL);
> +	if (!dfl)
> +		return -ENOMEM;
> +
> +	dfl->start = start;
> +	dfl->len = len;
> +	dfl->ioaddr = ioaddr;
> +
> +	list_add_tail(&dfl->node, &info->dfls);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(fpga_enum_info_add_dfl);
> +
> +void fpga_enum_info_free(struct fpga_enum_info *info)
> +{
> +	struct fpga_enum_dfl *tmp, *dfl;
> +	struct device *dev;
> +
> +	if (!info)
> +		return;
> +
> +	dev = info->dev;
> +
> +	/* remove all device feature lists in the list. */
> +	list_for_each_entry_safe(dfl, tmp, &info->dfls, node) {
> +		list_del(&dfl->node);
> +		devm_kfree(dev, dfl);
> +	}
> +
> +	devm_kfree(dev, info);
> +	put_device(dev);
> +}
> +EXPORT_SYMBOL_GPL(fpga_enum_info_free);
> +
> +static int remove_feature_dev(struct device *dev, void *data)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	enum fpga_id_type type = feature_dev_id_type(pdev);
> +	int id = pdev->id;
> +
> +	platform_device_unregister(pdev);
> +
> +	free_fpga_id(type, id);
> +
> +	return 0;
> +}
> +
> +static void remove_feature_devs(struct fpga_cdev *cdev)
> +{
> +	device_for_each_child(&cdev->region.dev, NULL, remove_feature_dev);
> +}
> +
> +/**
> + * fpga_enumerate_feature_devs - enumerate feature devices
> + * @info: information for enumeration.
> + *
> + * This function creates a container device (base FPGA region), enumerates
> + * feature devices based on the enumeration info and creates platform devices
> + * under the container device.
> + *
> + * Return: fpga_cdev struct on success, -errno on failure
> + */
> +struct fpga_cdev *fpga_enumerate_feature_devs(struct fpga_enum_info *info)
> +{
> +	struct build_feature_devs_info *binfo;
> +	struct fpga_cdev *cdev;
> +	struct fpga_enum_dfl *dfl;
> +	int ret = 0;
> +
> +	if (!info->dev)
> +		return ERR_PTR(-ENODEV);
> +
> +	cdev = devm_kzalloc(info->dev, sizeof(*cdev), GFP_KERNEL);
> +	if (!cdev)
> +		return ERR_PTR(-ENOMEM);
> +
> +	cdev->parent = info->dev;
> +	mutex_init(&cdev->lock);
> +	INIT_LIST_HEAD(&cdev->port_dev_list);
> +	cdev->region.parent = info->dev;
> +
> +	ret = fpga_region_register(&cdev->region);
> +	if (ret)
> +		goto free_cdev_exit;
> +
> +	/* create and init build info for enumeration */
> +	binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL);
> +	if (!binfo) {
> +		ret = -ENOMEM;
> +		goto unregister_region_exit;
> +	}
> +
> +	binfo->dev = info->dev;
> +	binfo->cdev = cdev;
> +
> +	/*
> +	 * start enumeration for all feature devices based on Device Feature
> +	 * Lists.
> +	 */
> +	list_for_each_entry(dfl, &info->dfls, node) {
> +		ret = parse_feature_list(binfo, dfl);
> +		if (ret) {
> +			remove_feature_devs(cdev);
> +			build_info_free(binfo);
> +			goto unregister_region_exit;
> +		}
> +	}
> +
> +	build_info_free(binfo);
> +
> +	return cdev;
> +
> +unregister_region_exit:
> +	fpga_region_unregister(&cdev->region);
> +free_cdev_exit:
> +	devm_kfree(cdev->parent, cdev);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(fpga_enumerate_feature_devs);
> +
> +/**
> + * fpga_remove_feature_devs - remove all feature devices
> + * @cdev: fpga container device.
> + *
> + * Remove the container device and all feature devices under given container
> + * devices.
> + */
> +void fpga_remove_feature_devs(struct fpga_cdev *cdev)
> +{
> +	struct feature_platform_data *pdata, *ptmp;
> +
> +	remove_feature_devs(cdev);
> +
> +	mutex_lock(&cdev->lock);
> +	if (cdev->fme_dev) {
> +		/* the fme should be unregistered. */
> +		WARN_ON(device_is_registered(cdev->fme_dev));
> +		put_device(cdev->fme_dev);
> +	}
> +
> +	list_for_each_entry_safe(pdata, ptmp, &cdev->port_dev_list, node) {
> +		struct platform_device *port_dev = pdata->dev;
> +
> +		/* the port should be unregistered. */
> +		WARN_ON(device_is_registered(&port_dev->dev));
> +		list_del(&pdata->node);
> +		put_device(&port_dev->dev);
> +	}
> +	mutex_unlock(&cdev->lock);
> +
> +	fpga_region_unregister(&cdev->region);
> +	devm_kfree(cdev->parent, cdev);
> +}
> +EXPORT_SYMBOL_GPL(fpga_remove_feature_devs);
> +
> +int fpga_port_id(struct platform_device *pdev)
> +{
> +	void __iomem *base;
> +
> +	base = get_feature_ioaddr_by_index(&pdev->dev, PORT_FEATURE_ID_HEADER);
> +	WARN_ON(!base);
> +
> +	return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + FME_HDR_CAP));
> +}
> +EXPORT_SYMBOL_GPL(fpga_port_id);
> +
> +/*
> + * Enable Port by clear the port soft reset bit, which is set by default.
> + * The User AFU is unable to respond to any MMIO access while in reset.
> + * __fpga_port_enable function should only be used after __fpga_port_disable
> + * function.
> + */
> +void __fpga_port_enable(struct platform_device *pdev)
> +{
> +	struct feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	void __iomem *base;
> +	u64 v;
> +
> +	WARN_ON(!pdata->disable_count);
> +
> +	if (--pdata->disable_count != 0)
> +		return;
> +
> +	base = get_feature_ioaddr_by_index(&pdev->dev, PORT_FEATURE_ID_HEADER);
> +	WARN_ON(!base);
> +
> +	/* Clear port soft reset */
> +	v = readq(base + PORT_HDR_CTRL);
> +	v &= ~PORT_CTRL_SFTRST;
> +	writeq(v, base + PORT_HDR_CTRL);
> +}
> +EXPORT_SYMBOL_GPL(__fpga_port_enable);
> +
> +#define RST_POLL_INVL 10 /* us */
> +#define RST_POLL_TIMEOUT 1000 /* us */
> +
> +int __fpga_port_disable(struct platform_device *pdev)
> +{
> +	struct feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	void __iomem *base;
> +	u64 v;
> +
> +	if (pdata->disable_count++ != 0)
> +		return 0;
> +
> +	base = get_feature_ioaddr_by_index(&pdev->dev, PORT_FEATURE_ID_HEADER);
> +	WARN_ON(!base);
> +
> +	/* Set port soft reset */
> +	v = readq(base + PORT_HDR_CTRL);
> +	v |= PORT_CTRL_SFTRST;
> +	writeq(v, base + PORT_HDR_CTRL);
> +
> +	/*
> +	 * HW sets ack bit to 1 when all outstanding requests have been drained
> +	 * on this port and minimum soft reset pulse width has elapsed.
> +	 * Driver polls port_soft_reset_ack to determine if reset done by HW.
> +	 */
> +	if (readq_poll_timeout(base + PORT_HDR_CTRL, v, v & PORT_CTRL_SFTRST,
> +			       RST_POLL_INVL, RST_POLL_TIMEOUT)) {
> +		dev_err(&pdev->dev, "timeout, fail to reset device\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(__fpga_port_disable);
> +
> +static int __init dfl_fpga_init(void)
> +{
> +	fpga_ids_init();
> +
> +	return 0;
> +}
> +
> +static void __exit dfl_fpga_exit(void)
> +{
> +	fpga_ids_destroy();
> +}
> +
> +module_init(dfl_fpga_init);
> +module_exit(dfl_fpga_exit);
> +
> +MODULE_DESCRIPTION("FPGA Device Feature List (DFL) Support");
> +MODULE_AUTHOR("Intel Corporation");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/fpga/fpga-dfl.h b/drivers/fpga/fpga-dfl.h
> new file mode 100644
> index 0000000..abcbe57
> --- /dev/null
> +++ b/drivers/fpga/fpga-dfl.h
> @@ -0,0 +1,365 @@
> +/*
> + * Driver Header File for FPGA Device Feature List (DFL) Support
> + *
> + * Copyright (C) 2017 Intel Corporation, Inc.
> + *
> + * Authors:
> + *   Kang Luwei <luwei.kang@...el.com>
> + *   Zhang Yi <yi.z.zhang@...el.com>
> + *   Wu Hao <hao.wu@...el.com>
> + *   Xiao Guangrong <guangrong.xiao@...ux.intel.com>
> + *
> + * This work is licensed under the terms of the GNU GPL version 2.
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#ifndef __DFL_FPGA_H
> +#define __DFL_FPGA_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_FPGA_PORT_NUM 4
> +/* plus one for fme device */
> +#define MAX_FEATURE_DEV_NUM	(MAX_FPGA_PORT_NUM + 1)
> +
> +#define FME_FEATURE_HEADER          "fme_hdr"
> +#define FME_FEATURE_THERMAL_MGMT    "fme_thermal"
> +#define FME_FEATURE_POWER_MGMT      "fme_power"
> +#define FME_FEATURE_GLOBAL_IPERF    "fme_iperf"
> +#define FME_FEATURE_GLOBAL_ERR      "fme_error"
> +#define FME_FEATURE_PR_MGMT         "fme_pr"
> +#define FME_FEATURE_HSSI            "fme_hssi"
> +#define FME_FEATURE_GLOBAL_DPERF    "fme_dperf"
> +
> +#define PORT_FEATURE_HEADER         "port_hdr"
> +#define PORT_FEATURE_UAFU           "port_uafu"
> +#define PORT_FEATURE_ERR            "port_err"
> +#define PORT_FEATURE_UMSG           "port_umsg"
> +#define PORT_FEATURE_PR             "port_pr"
> +#define PORT_FEATURE_STP            "port_stp"
> +
> +/* Device Feature Header Register Set */
> +#define DFH			0x0
> +#define GUID_L			0x8
> +#define GUID_H			0x10
> +#define NEXT_AFU		0x18
> +
> +/* 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(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 */
> +
> +/*
> + * It only has DFH register as header for private feature, but for FIU/AFU
> + * For FIU/AFU features, they all have DFH + GUID + NEXT_AFU as common header
> + * registers.
> + */
> +#define DFH_SIZE		0x8
> +#define AFU_DFH_SIZE		0x20
> +#define FIU_DFH_SIZE		0x20
> +
> +/* FME Header Register Set */
> +#define FME_HDR_DFH		DFH
> +#define FME_HDR_AFU_GUID_L	GUID_L
> +#define FME_HDR_AFU_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
> +#define FME_HDR_SIZE		0x70
> +
> +/* FME Fab Capability Register Bitfield */
> +#define FME_CAP_FABRIC_VERID	GENMASK_ULL(7, 0)	/* Fabric version ID */
> +#define FME_CAP_SOCKET_ID	BIT(8)			/* Socket ID */
> +#define FME_CAP_PCIE0_LINK_AVL	BIT(12)			/* PCIE0 Link */
> +#define FME_CAP_PCIE1_LINK_AVL	BIT(13)			/* PCIE1 Link */
> +#define FME_CAP_COHR_LINK_AVL	BIT(14)			/* Coherent Link */
> +#define FME_CAP_IOMMU_AVL	BIT(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(55)
> +#define FME_PORT_OFST_ACC_PF	0
> +#define FME_PORT_OFST_ACC_VF	1
> +#define FME_PORT_OFST_IMP	BIT(60)
> +
> +/* FME Thermal Sub Feature Register Set */
> +#define FME_THERMAL_DFH		DFH
> +#define FME_THERMAL_SIZE	DFH_SIZE
> +
> +/* FME Power Sub Feature Register Set */
> +#define FME_POWER_DFH		DFH
> +#define FME_POWER_SIZE		DFH_SIZE
> +
> +/* FME Global Performance Sub Feature Register Set */
> +#define FME_IPERF_DFH		DFH
> +#define FME_IPERF_SIZE		DFH_SIZE
> +
> +/* FME Global Error Sub Feature Register Set */
> +#define FME_ERR_DFH		DFH
> +#define FME_ERR_SIZE		DFH_SIZE
> +
> +/* FME Partial Reconfiguration Sub Feature Register Set */
> +#define FME_PR_DFH		DFH
> +#define FME_PR_SIZE		DFH_SIZE
> +
> +/* FME HSSI Sub Feature Register Set */
> +#define FME_HSSI_DFH		DFH
> +#define FME_HSSI_SIZE		DFH_SIZE
> +
> +/* FME Global Performance Sub Feature Register Set */
> +#define FME_DPERF_DFH		DFH
> +#define FME_DPERF_SIZE		DFH_SIZE
> +
> +/* PORT Header Register Set */
> +#define PORT_HDR_DFG		DFH
> +#define PORT_HDR_AFU_GUID_L	GUID_L
> +#define PORT_HDR_AFU_GUID_H	GUID_H
> +#define PORT_HDR_NEXT_AFU	NEXT_AFU
> +#define PORT_HDR_CAP		0x30
> +#define PORT_HDR_CTRL		0x38
> +#define PORT_HDR_SIZE		0x40
> +
> +/* Port Capability Register Bitfield */
> +#define PORT_CAP_PORT_NUM	GENMASK(1, 0)		/* ID of this port */
> +#define PORT_CAP_MMIO_SIZE	GENMASK(23, 8)		/* MMIO size in KB */
> +#define PORT_CAP_SUPP_INT_NUM	GENMASK(35, 32)		/* Interrupts num */
> +
> +/* Port Control Register Bitfield */
> +#define PORT_CTRL_SFTRST	BIT(0)			/* Port soft reset */
> +/* Latency tolerance reporting. '1' >= 40us, '0' < 40us.*/
> +#define PORT_CTRL_LATENCY	BIT(2)
> +#define PORT_CTRL_SFTRST_ACK	BIT(4)			/* HW ack for reset */
> +
> +/* PORT Error Sub Feature Register Set */
> +#define PORT_ERR_DFH		DFH
> +#define PORT_ERR_SIZE		DFH_SIZE
> +
> +/* PORT Unordered Message Sub Feature Register Set */
> +#define PORT_UMSG_DFH		DFH
> +#define PORT_UMSG_SIZE		DFH_SIZE
> +
> +/* PORT SignalTap Sub Feature Register Set */
> +#define PORT_STP_DFH		DFH
> +#define PORT_STP_SIZE		DFH_SIZE
> +
> +/* PORT User AFU Sub Feature Register Set */
> +#define PORT_UAFU_DFH		DFH
> +#define PORT_UAFU_SIZE		DFH_SIZE
> +
> +struct feature {
> +	const char *name;
> +	int resource_index;
> +	void __iomem *ioaddr;
> +};
> +
> +struct feature_platform_data {
> +	/* list the feature dev to cci_drvdata->port_dev_list. */
> +	struct list_head node;
> +	struct mutex lock;		/* protect platform data */
> +	struct platform_device *dev;
> +	unsigned int disable_count;	/* count for port disable */
> +
> +	int num;			/* number of features */
> +	struct feature features[0];
> +};
> +
> +enum fme_feature_id {
> +	FME_FEATURE_ID_HEADER = 0x0,
> +	FME_FEATURE_ID_THERMAL_MGMT = 0x1,
> +	FME_FEATURE_ID_POWER_MGMT = 0x2,
> +	FME_FEATURE_ID_GLOBAL_IPERF = 0x3,
> +	FME_FEATURE_ID_GLOBAL_ERR = 0x4,
> +	FME_FEATURE_ID_PR_MGMT = 0x5,
> +	FME_FEATURE_ID_HSSI = 0x6,
> +	FME_FEATURE_ID_GLOBAL_DPERF = 0x7,
> +	FME_FEATURE_ID_MAX = 0x8,
> +};
> +
> +enum port_feature_id {
> +	PORT_FEATURE_ID_HEADER = 0x0,
> +	PORT_FEATURE_ID_ERROR = 0x1,
> +	PORT_FEATURE_ID_UMSG = 0x2,
> +	PORT_FEATURE_ID_PR = 0x3,
> +	PORT_FEATURE_ID_STP = 0x4,
> +	PORT_FEATURE_ID_UAFU = 0x5,
> +	PORT_FEATURE_ID_MAX = 0x6,
> +};
> +
> +#define FME_FEATURE_NUM			FME_FEATURE_ID_MAX
> +#define PORT_FEATURE_NUM		PORT_FEATURE_ID_MAX
> +
> +#define FPGA_FEATURE_DEV_FME		"fpga-dfl-fme"
> +#define FPGA_FEATURE_DEV_PORT		"fpga-dfl-port"
> +
> +static inline int feature_platform_data_size(const int num)
> +{
> +	return sizeof(struct feature_platform_data) +
> +		num * sizeof(struct feature);
> +}
> +
> +int fpga_port_id(struct platform_device *pdev);
> +
> +static inline int fpga_port_check_id(struct platform_device *pdev,
> +				     void *pport_id)
> +{
> +	return fpga_port_id(pdev) == *(int *)pport_id;
> +}
> +
> +void __fpga_port_enable(struct platform_device *pdev);
> +int __fpga_port_disable(struct platform_device *pdev);
> +
> +static inline void fpga_port_enable(struct platform_device *pdev)
> +{
> +	struct feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +
> +	mutex_lock(&pdata->lock);
> +	__fpga_port_enable(pdev);
> +	mutex_unlock(&pdata->lock);
> +}
> +
> +static inline int fpga_port_disable(struct platform_device *pdev)
> +{
> +	struct feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	int ret;
> +
> +	mutex_lock(&pdata->lock);
> +	ret = __fpga_port_disable(pdev);
> +	mutex_unlock(&pdata->lock);
> +
> +	return ret;
> +}
> +
> +static inline int __fpga_port_reset(struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	ret = __fpga_port_disable(pdev);
> +	if (ret)
> +		return ret;
> +
> +	__fpga_port_enable(pdev);
> +
> +	return 0;
> +}
> +
> +static inline int fpga_port_reset(struct platform_device *pdev)
> +{
> +	struct feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
> +	int ret;
> +
> +	mutex_lock(&pdata->lock);
> +	ret = __fpga_port_reset(pdev);
> +	mutex_unlock(&pdata->lock);
> +
> +	return ret;
> +}
> +
> +static inline void __iomem *
> +get_feature_ioaddr_by_index(struct device *dev, int index)
> +{
> +	struct feature_platform_data *pdata = dev_get_platdata(dev);
> +
> +	return pdata->features[index].ioaddr;
> +}
> +
> +static inline bool 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 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);
> +}
> +
> +/**
> + * fpga_enum_info - FPGA enumeration information
> + *
> + * @dev: parent device.
> + * @dfls: list of device feature lists.
> + */
> +struct fpga_enum_info {
> +	struct device *dev;
> +	struct list_head dfls;
> +};
> +
> +/**
> + * fpga_enum_dfl - FPGA enumeration device feature list information
> + *
> + * @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 fpga_enum_dfl {
> +	resource_size_t start;
> +	resource_size_t len;
> +
> +	void __iomem *ioaddr;
> +
> +	struct list_head node;
> +};
> +
> +struct fpga_enum_info *fpga_enum_info_alloc(struct device *dev);
> +int fpga_enum_info_add_dfl(struct fpga_enum_info *info, resource_size_t start,
> +			   resource_size_t len, void __iomem *ioaddr);
> +void fpga_enum_info_free(struct fpga_enum_info *info);
> +
> +/**
> + * fpga_cdev - fpga container device
> + * @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 fpga_cdev {
> +	struct device *parent;
> +
> +	struct fpga_region region;
> +
> +	struct device *fme_dev;
> +
> +	struct mutex lock; /* to protect the port device list */
> +	struct list_head port_dev_list;
> +};
> +
> +struct fpga_cdev *fpga_enumerate_feature_devs(struct fpga_enum_info *info);
> +void fpga_remove_feature_devs(struct fpga_cdev *cdev);
> +
> +#endif /* __DFL_FPGA_H */
> -- 
> 1.8.3.1
> 

Thanks,

Moritz

Powered by blists - more mailing lists