[v1] baseband/fpga_5gnr_fec: add companion PF config App
diff mbox series

Message ID 1594419966-230753-2-git-send-email-nicolas.chautru@intel.com
State Superseded, archived
Delegated to: akhil goyal
Headers show
Series
  • [v1] baseband/fpga_5gnr_fec: add companion PF config App
Related show

Checks

Context Check Description
ci/iol-testing success Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/Intel-compilation success Compilation OK
ci/travis-robot success Travis build: passed
ci/Performance-Testing fail build patch failure
ci/checkpatch warning coding style issues

Commit Message

Chautru, Nicolas July 10, 2020, 10:26 p.m. UTC
Adding companion application to configure HW Device from the PF.
Then the device can be accessed through BBDEV from VF (or PF).

Signed-off-by: Nicolas Chautru <nicolas.chautru@intel.com>
---
 doc/guides/bbdevs/fpga_5gnr_fec.rst                |  81 +++--
 .../baseband/fpga_5gnr_fec/pf_config_app/Makefile  |  36 ++
 .../fpga_5gnr_fec/pf_config_app/config_app.c       | 382 +++++++++++++++++++++
 .../pf_config_app/fpga_5gnr_cfg_app.c              | 351 +++++++++++++++++++
 .../pf_config_app/fpga_5gnr_cfg_app.h              | 102 ++++++
 .../pf_config_app/fpga_5gnr_cfg_parser.c           | 187 ++++++++++
 .../pf_config_app/fpga_5gnr_config.cfg             |  18 +
 7 files changed, 1137 insertions(+), 20 deletions(-)
 create mode 100644 drivers/baseband/fpga_5gnr_fec/pf_config_app/Makefile
 create mode 100644 drivers/baseband/fpga_5gnr_fec/pf_config_app/config_app.c
 create mode 100644 drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.c
 create mode 100644 drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.h
 create mode 100644 drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_parser.c
 create mode 100644 drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_config.cfg

Comments

Akhil Goyal July 16, 2020, 8:56 a.m. UTC | #1
Hi Nicholas,

> Subject: [PATCH v1] baseband/fpga_5gnr_fec: add companion PF config App
> 
> Adding companion application to configure HW Device from the PF.
> Then the device can be accessed through BBDEV from VF (or PF).
> 
> Signed-off-by: Nicolas Chautru <nicolas.chautru@intel.com>
> ---

I believe this patch is for next release as it came late in this release cycle.

Regards,
Akhil
David Marchand July 16, 2020, 9:40 a.m. UTC | #2
On Sat, Jul 11, 2020 at 12:28 AM Nicolas Chautru
<nicolas.chautru@intel.com> wrote:

[snip]

> +#define SYS_DIR "/sys/bus/pci/devices"
> +#define CUR_DIR "."
> +#define PREV_DIR ".."
> +
> +#define DRIVER_LINK  "driver"
> +#define DEVICE_FILE  "device"
> +#define VENDOR_FILE  "vendor"
> +#define BAR0_FILE    "resource0"
> +#define MAX_VFS_FILE "max_vfs"
> +
> +#define PCI_STR_SIZE 15
> +#define DEV_STR_SIZE 10
> +#define NULL_PAD     2
> +
> +/* Function Pointer for device specific configuration file */
> +typedef int (*configuration)(void *bar0addr, const char *arg_cfg_filename);
> +
> +typedef struct hw_device {
> +       const char *device_name;
> +       char *config_file;
> +       int vendor_id;
> +       int device_id;
> +       char pci_address[PCI_STR_SIZE];
> +       bool driver_found;
> +       configuration conf;
> +       char *num_vfs;
> +       int config_all;
> +} hw_device;
> +
> +static int
> +enable_vfs(const char *pci_addr, char *num_vfs)
> +{
> +       char maxvfspath[PATH_MAX];
> +       char fs_num_vfs[4] = {0, 0, 0, 0};
> +       int maxvfsfd;
> +
> +       snprintf(maxvfspath, sizeof(maxvfspath),
> +                       "%s/%s/%s", SYS_DIR, pci_addr, MAX_VFS_FILE);
> +       maxvfsfd = open(maxvfspath, O_RDWR | O_SYNC);

You should stop relying on igb_uio (reminder: it is going to move out
of the dpdk tree in 20.11) and use vfio vf token that got merged in
20.08.
Chautru, Nicolas July 16, 2020, 6:14 p.m. UTC | #3
> From: Akhil Goyal <akhil.goyal@nxp.com>
> Hi Nicholas,
> 
> > Subject: [PATCH v1] baseband/fpga_5gnr_fec: add companion PF config App
> >
> > Adding companion application to configure HW Device from the PF.
> > Then the device can be accessed through BBDEV from VF (or PF).
> >
> > Signed-off-by: Nicolas Chautru <nicolas.chautru@intel.com>
> > ---
> 
> I believe this patch is for next release as it came late in this release cycle.
> 

Understood Akhil, I will mention explicitly 20.11 in my v2. 
For now I wanted to make sure this is matching with previous discussion with Thomas and others + get some acks prior to next cycle.
Chautru, Nicolas July 16, 2020, 7:59 p.m. UTC | #4
> From: David Marchand <david.marchand@redhat.com>
> On Sat, Jul 11, 2020 at 12:28 AM Nicolas Chautru <nicolas.chautru@intel.com>
> wrote:
> 
> [snip]
> 
> > +#define SYS_DIR "/sys/bus/pci/devices"
> > +#define CUR_DIR "."
> > +#define PREV_DIR ".."
> > +
> > +#define DRIVER_LINK  "driver"
> > +#define DEVICE_FILE  "device"
> > +#define VENDOR_FILE  "vendor"
> > +#define BAR0_FILE    "resource0"
> > +#define MAX_VFS_FILE "max_vfs"
> > +
> > +#define PCI_STR_SIZE 15
> > +#define DEV_STR_SIZE 10
> > +#define NULL_PAD     2
> > +
> > +/* Function Pointer for device specific configuration file */ typedef
> > +int (*configuration)(void *bar0addr, const char *arg_cfg_filename);
> > +
> > +typedef struct hw_device {
> > +       const char *device_name;
> > +       char *config_file;
> > +       int vendor_id;
> > +       int device_id;
> > +       char pci_address[PCI_STR_SIZE];
> > +       bool driver_found;
> > +       configuration conf;
> > +       char *num_vfs;
> > +       int config_all;
> > +} hw_device;
> > +
> > +static int
> > +enable_vfs(const char *pci_addr, char *num_vfs) {
> > +       char maxvfspath[PATH_MAX];
> > +       char fs_num_vfs[4] = {0, 0, 0, 0};
> > +       int maxvfsfd;
> > +
> > +       snprintf(maxvfspath, sizeof(maxvfspath),
> > +                       "%s/%s/%s", SYS_DIR, pci_addr, MAX_VFS_FILE);
> > +       maxvfsfd = open(maxvfspath, O_RDWR | O_SYNC);
> 
> You should stop relying on igb_uio (reminder: it is going to move out of the
> dpdk tree in 20.11) and use vfio vf token that got merged in 20.08.
> 

Thanks David. 
We will keep the VF creation externally so that to any avoid such dependency on which kernel variant ends up being used. 
The VF creation was kept here as an option for ease of use but not really required as non device specific. 
The main feature is purely configuration from MMIO write.

Patch
diff mbox series

diff --git a/doc/guides/bbdevs/fpga_5gnr_fec.rst b/doc/guides/bbdevs/fpga_5gnr_fec.rst
index 19bba36..b9333e3 100644
--- a/doc/guides/bbdevs/fpga_5gnr_fec.rst
+++ b/doc/guides/bbdevs/fpga_5gnr_fec.rst
@@ -218,33 +218,74 @@  parameters defined in ``fpga_5gnr_fec_conf`` structure:
   time_out = flr_time_out x 16.384us. For instance, if you want to set 10ms for
   the FLR time out then set this setting to 0x262=610.
 
+A companion application pf_config_app is provided as a standalone application
+described in next section. 
 
-An example configuration code calling the function ``fpga_5gnr_fec_configure()`` is shown
-below:
+PF Config App
+-------------
 
-.. code-block:: c
+The PF Configuration Application ``pf_config_app`` provides a means to
+configure the baseband device at the host-level. The program sets the various
+parameters through memory-mapped IO read/writes. Then the BBDEV driver can be
+used to run the workload.
+
+The parameters are parsed from a given configuration file (with .cfg extentions)
+that is specific to particular BBDEV device, although they follow same format.
+
+External Dependencies
+~~~~~~~~~~~~~~~~~~~~~
+
+The third party .INI parser is a pre-requisite for building the application.
+It can be downloaded from [github]:
+
+.. code-block:: console
 
-  struct fpga_5gnr_fec_conf conf;
-  unsigned int i;
+    git clone https://github.com/benhoyt/inih
 
-  memset(&conf, 0, sizeof(struct fpga_5gnr_fec_conf));
-  conf.pf_mode_en = 1;
+Use the version release 44 tracked by tag 'r44':
+
+.. code-block:: console
+
+    git checkout r44
+
+The application features a makefile in the `extra/` directory which generates
+the library, `libinih.a`. To compile the inih library, run make as:
+
+.. code-block:: console
+
+    make -f Makefile.static
+
+Building PF Config App
+~~~~~~~~~~~~~~~~~~~~~~
+
+Before building the application, set the following environment variables with
+the location where the INI library is located:
+
+.. code-block:: console
+
+    export INIH_PATH=<path-to-inih-lib>
+
+If not set, makefile will look into current folder.
+
+Next, build the program by typing ``make`` from the pf_Config_app subdirectory
+to produce the binary ``pf_config_app_fpga_5gnr``.
+
+Usage
+~~~~~
+
+The application executes as the following:
+
+.. code-block:: console
 
-  for (i = 0; i < FPGA_5GNR_FEC_NUM_VFS; ++i) {
-      conf.vf_ul_queues_number[i] = 4;
-      conf.vf_dl_queues_number[i] = 4;
-  }
-  conf.ul_bandwidth = 12;
-  conf.dl_bandwidth = 5;
-  conf.dl_load_balance = 64;
-  conf.ul_load_balance = 64;
+    ./pf_config_app_fpga_5gnr [-h] [-a] [-c CFG_FILE] [-f NUM_VFS] [-p PCI_ID]
 
-  /* setup FPGA PF */
-  ret = fpga_5gnr_fec_configure(info->dev_name, &conf);
-  TEST_ASSERT_SUCCESS(ret,
-      "Failed to configure 4G FPGA PF for bbdev %s",
-      info->dev_name);
+* ``-c CFG_FILE``: Specifies configuration file to use
+* ``-f NUM_VFS``: Specifies number of Virtual Functions to enable through SRIOV
+* ``-p PCI_ID``: Specifies PCI ID of device to configure
+* ``-a``: Configures all PCI devices
+* ``-h``: Prints help
 
+Default configure file is provided: ``fpga_5gnr_config.cfg``.
 
 Test Application
 ----------------
diff --git a/drivers/baseband/fpga_5gnr_fec/pf_config_app/Makefile b/drivers/baseband/fpga_5gnr_fec/pf_config_app/Makefile
new file mode 100644
index 0000000..dfd0b6d
--- /dev/null
+++ b/drivers/baseband/fpga_5gnr_fec/pf_config_app/Makefile
@@ -0,0 +1,36 @@ 
+
+CC=gcc
+CFLAGS=-O0 -g -Wall
+ODIR=build
+DEPS=
+
+ifeq ($(INIH_PATH),)
+INCLUDE=-I.
+LDFLAGS=-L.
+else
+INCLUDE=-I. -I$(INIH_PATH)
+LDFLAGS=-L. -L$(INIH_PATH)
+endif
+
+LDLIBS=-linih
+
+SRC = config_app.c fpga_5gnr_cfg_app.c fpga_5gnr_cfg_parser.c
+OBJ = $(patsubst %.c,$(ODIR)/%.o,$(SRC))
+
+.PHONY: clean
+
+all: pf_config_app
+
+$(ODIR):
+	mkdir -p $(ODIR)
+
+$(OBJ): $(ODIR)/%.o: ./%.c | $(DEPS) $(ODIR)
+	@mkdir -p $(@D)
+	$(CC) -c -o $@ $< $(CFLAGS) $(INCLUDE)
+
+pf_config_app: $(OBJ)
+	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
+
+clean:
+	rm -rf $(ODIR)
+	rm -rf pf_config_app
diff --git a/drivers/baseband/fpga_5gnr_fec/pf_config_app/config_app.c b/drivers/baseband/fpga_5gnr_fec/pf_config_app/config_app.c
new file mode 100644
index 0000000..61036fc
--- /dev/null
+++ b/drivers/baseband/fpga_5gnr_fec/pf_config_app/config_app.c
@@ -0,0 +1,382 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <linux/vfio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "fpga_5gnr_cfg_app.h"
+
+#define SYS_DIR "/sys/bus/pci/devices"
+#define CUR_DIR "."
+#define PREV_DIR ".."
+
+#define DRIVER_LINK  "driver"
+#define DEVICE_FILE  "device"
+#define VENDOR_FILE  "vendor"
+#define BAR0_FILE    "resource0"
+#define MAX_VFS_FILE "max_vfs"
+
+#define PCI_STR_SIZE 15
+#define DEV_STR_SIZE 10
+#define NULL_PAD     2
+
+/* Function Pointer for device specific configuration file */
+typedef int (*configuration)(void *bar0addr, const char *arg_cfg_filename);
+
+typedef struct hw_device {
+	const char *device_name;
+	char *config_file;
+	int vendor_id;
+	int device_id;
+	char pci_address[PCI_STR_SIZE];
+	bool driver_found;
+	configuration conf;
+	char *num_vfs;
+	int config_all;
+} hw_device;
+
+static int
+enable_vfs(const char *pci_addr, char *num_vfs)
+{
+	char maxvfspath[PATH_MAX];
+	char fs_num_vfs[4] = {0, 0, 0, 0};
+	int maxvfsfd;
+
+	snprintf(maxvfspath, sizeof(maxvfspath),
+			"%s/%s/%s", SYS_DIR, pci_addr, MAX_VFS_FILE);
+	maxvfsfd = open(maxvfspath, O_RDWR | O_SYNC);
+
+	if (maxvfsfd < 0) {
+		printf("Unable to enable VFs. Was device bound under igb_uio?\n");
+		return -1;
+	}
+
+	/* read current num of VFs */
+	read(maxvfsfd, (void *)fs_num_vfs, 3);
+	strtok(fs_num_vfs, "\n");
+
+	if (!strncmp(fs_num_vfs, num_vfs, 3)) {
+		/* value is same */
+		close(maxvfsfd);
+		return 0;
+	}
+
+	/* update num of VFs */
+	write(maxvfsfd, "0", 1);
+	write(maxvfsfd, num_vfs, strlen(num_vfs));
+	close(maxvfsfd);
+
+	return 0;
+}
+
+static void *
+get_bar0_mapping(const char *pci_addr, unsigned int bar_size)
+{
+	char bar0path[PATH_MAX];
+	int bar0addrfd;
+	void *map;
+
+	snprintf(bar0path, sizeof(bar0path),
+			"%s/%s/%s", SYS_DIR, pci_addr, BAR0_FILE);
+	bar0addrfd = open(bar0path, O_RDWR | O_SYNC);
+	if (bar0addrfd < 0) {
+		printf("\nFailed to open BAR %s\n", bar0path);
+		exit(1);
+	}
+	map = mmap(0, bar_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+			bar0addrfd, 0);
+	close(bar0addrfd);
+	return map;
+}
+
+static unsigned long
+get_file_val(const char *file_path)
+{
+	char content[BUFSIZ];
+	FILE *f;
+
+	f = fopen(file_path, "r");
+	if (f == NULL) {
+		printf("\nFailed to open %s\n", file_path);
+		exit(1);
+	}
+	if (fgets(content, sizeof(content), f) == NULL) {
+		fclose(f);
+		return false;
+	}
+	fclose(f);
+	const unsigned long content_val = strtoul(content, NULL, 16);
+
+	return content_val;
+}
+
+static bool
+get_device_id(hw_device *device, const char *location)
+{
+	unsigned long vendor_id = -1, device_id = -1;
+	struct dirent *dirent;
+	DIR *dir;
+	char pci_path[PATH_MAX];
+
+	snprintf(pci_path, sizeof(pci_path), "%s/%s", SYS_DIR, location);
+	dir = opendir(pci_path);
+	if (dir == NULL) {
+		printf("Failed to open %s (%s)\n", pci_path, strerror(errno));
+		return false;
+	}
+
+	while ((dirent = readdir(dir)) != NULL) {
+		char file_path[PATH_MAX];
+
+		/* Omit Current Directory & Previous Directory Lookup */
+		if (strncmp(dirent->d_name, CUR_DIR,
+				strlen(dirent->d_name)) == 0 ||
+				strncmp(dirent->d_name, PREV_DIR,
+				strlen(dirent->d_name)) == 0)
+			continue;
+
+		/* Set filepath as next PCI folder (xxxx:xx:xx.x) */
+		snprintf(file_path, sizeof(file_path), "%s/%s",
+				pci_path, dirent->d_name);
+
+		/* Get Device ID */
+		if (strncmp(dirent->d_name, DEVICE_FILE,
+				strlen(dirent->d_name)) == 0 &&
+				dirent->d_type == DT_REG)
+			device_id = get_file_val(file_path);
+
+		/* Get Vendor ID */
+		if (strncmp(dirent->d_name, VENDOR_FILE,
+				strlen(dirent->d_name)) == 0 &&
+				dirent->d_type == DT_REG)
+			vendor_id = get_file_val(file_path);
+	}
+
+	closedir(dir);
+	/* Check if device is found */
+	return (vendor_id == device->vendor_id &&
+			device_id == device->device_id);
+}
+
+static int
+probe_pci_bus(hw_device *device, char **found_devices)
+{
+	struct dirent *dirent;
+	DIR *dir;
+	int num_devices = 0;
+
+	/* Locate PCI Devices */
+	dir = opendir(SYS_DIR);
+	if (dir == NULL)
+		return -1;
+
+	/* Iterate Through Directories */
+	while ((dirent = readdir(dir)) != NULL) {
+
+		/* Omit Current Directory and Previous Directory Lookup */
+		if (strncmp(dirent->d_name, CUR_DIR,
+				strlen(dirent->d_name)) == 0 ||
+				strncmp(dirent->d_name, PREV_DIR,
+						strlen(dirent->d_name)) == 0)
+			continue;
+		/* Check if current device matches requested device */
+		if (get_device_id(device, dirent->d_name)) {
+			found_devices[num_devices] =
+					(char *) malloc(PCI_STR_SIZE);
+			/* Copy PCI slot of device */
+			strncpy(found_devices[num_devices], dirent->d_name,
+					sizeof(device->pci_address) - NULL_PAD);
+			num_devices++;
+		}
+	}
+
+	return num_devices;
+}
+
+static int
+match_device(char *pci_address, char **found_devices, int num_devices)
+{
+	int i;
+	for (i = 0; i < num_devices; i++) {
+		if (!strncmp(pci_address, found_devices[i],
+				sizeof(pci_address) - NULL_PAD))
+			return 0;
+	}
+
+	printf("Given PCI ID is not available\n");
+	return -1;
+}
+
+static int
+select_device(hw_device *device, char **found_devices, int num_devices)
+{
+	int i, selected;
+	/* If more than one device found, get user input on which to use */
+	if (num_devices >= 2) {
+		printf("More than one device found. Please select which device you would like to use from the list:\n");
+
+		/*Print PCI Slots */
+		for (i = 0; i < num_devices; i++)
+			printf("[%i]: %s\n", i+1, found_devices[i]);
+
+		printf("> ");
+		scanf("%d", &selected);
+		if (selected >= 1 && selected <= num_devices) {
+			strncpy(device->pci_address, found_devices[selected-1],
+					sizeof(device->pci_address) - NULL_PAD);
+			return 0;
+		}
+
+		printf("Invalid Option, please try again..\n");
+		return -1;
+	}
+
+	strncpy(device->pci_address, found_devices[0],
+			sizeof(device->pci_address) - NULL_PAD);
+	return 0;
+}
+
+void set_device(hw_device *device)
+{
+	device->vendor_id = FPGA_5GNR_FEC_VENDOR_ID;
+	device->device_id = FPGA_5GNR_FEC_DEVICE_ID;
+	device->conf = fpga_5gnr_configure;
+	if (device->config_file == NULL) {
+		device->config_file = getenv("FPGA_5GNR_CONFIG_FILE");
+		if (device->config_file == NULL)
+			device->config_file = "fpga_5gnr_config.cfg";
+	}
+}
+
+static void
+print_helper(const char *prgname)
+{
+	printf("Usage: %s [-h] [-a] [-c CFG_FILE] [-f NUM_VFS] [-p PCI_ID]\n\n"
+			" -c CFG_FILE \t specifies configuration file to use\n"
+			" -f NUM_VFS \t specifies number of Virtual Functions to enable through SRIOV\n"
+			" -p PCI_ID \t specifies PCI ID of device to configure\n"
+			" -a \t\t configures all PCI devices matching the given DEVICE_NAME\n"
+			" -h \t\t prints this helper\n\n", prgname);
+}
+
+static int
+parse_args(int argc, char **argv,
+		struct hw_device *device)
+{
+	int opt;
+	char *prgname = argv[0];
+	device->device_name = FPGA_5GNR_FEC_DEV_NAME;
+
+	while ((opt = getopt(argc, argv, "c:f:p:ah")) != -1) {
+		switch (opt) {
+		case 'c':
+			device->config_file = optarg;
+			break;
+		case 'f':
+			device->num_vfs = optarg;
+			break;
+		case 'p':
+			strncpy(device->pci_address, optarg,
+					sizeof(device->pci_address)
+					- NULL_PAD);
+			break;
+		case 'a':
+			device->config_all = 1;
+			break;
+		case 'h':
+		default:
+			print_helper(prgname);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+configure_device(hw_device *device)
+{
+	/* Get BAR0 Mapping for device */
+	void *bar0addr = get_bar0_mapping(device->pci_address,
+			FPGA_5GNR_FEC_BAR_SIZE);
+
+	if (device->num_vfs != 0)
+		if (enable_vfs(device->pci_address, device->num_vfs) < 0)
+			return -1;
+
+	/* Call device specific configuration function */
+	if (device->conf(bar0addr, device->config_file) == 0) {
+
+		if (device->num_vfs != 0)
+			printf("Enabled %s %s VFs\n",
+					device->num_vfs, device->device_name);
+
+		printf("%s PF [%s] configuration complete!\n\n",
+				device->device_name, device->pci_address
+				- NULL_PAD);
+		return 0;
+	}
+	printf("Configuration error!!\n");
+	return -1;
+}
+
+int
+main(int argc, char *argv[])
+{
+	int i, num_devices;
+	hw_device device;
+	char *found_devices[DEV_STR_SIZE];
+
+	memset(&device, 0, sizeof(device));
+
+	if (parse_args(argc, argv, &device) > 0)
+		return 0;
+
+	/* Set Device Info */
+	set_device(&device);
+	/* Check if device is installed */
+	num_devices = probe_pci_bus(&device, found_devices);
+	if (num_devices == 0) {
+		printf("No devices found!!\n");
+		return -1;
+	} else if (num_devices < 0) {
+		return num_devices;
+	}
+
+	if (device.pci_address[0] != 0) {
+		if (match_device(device.pci_address, found_devices,
+				num_devices) < 0)
+			return -1;
+	}
+
+	if (device.config_all) {
+		for (i = 0; i < num_devices; i++) {
+			strncpy(device.pci_address, found_devices[i],
+					sizeof(device.pci_address) - NULL_PAD);
+			configure_device(&device);
+		}
+	} else {
+		select_device(&device, found_devices, num_devices);
+		configure_device(&device);
+	}
+
+	/* Free memory for stored PCI slots */
+	for (i = 0; i < num_devices; i++)
+		free(found_devices[i]);
+
+	return 0;
+}
diff --git a/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.c b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.c
new file mode 100644
index 0000000..ac01722
--- /dev/null
+++ b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.c
@@ -0,0 +1,351 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "fpga_5gnr_cfg_app.h"
+
+extern int
+fpga_5gnr_parse_conf_file(const char *file_name,
+		struct fpga_5gnr_fec_conf *fpga_conf);
+
+/* Read 8-bit register of FPGA 5GNR FEC device */
+static uint8_t
+fpga_reg_read_8(void *mmio_base, uint32_t offset)
+{
+	void *reg_addr = mmio_base + offset;
+	return *((volatile uint8_t *)(reg_addr));
+}
+
+/* Read 16-bit register of FPGA 5GNR FEC device */
+static uint16_t
+fpga_reg_read_16(void *mmio_base, uint32_t offset)
+{
+	void *reg_addr = mmio_base + offset;
+	uint16_t ret = *((volatile uint16_t *)(reg_addr));
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+	return __bswap_16(ret);
+#else
+	return ret;
+#endif
+}
+
+/* Read 32-bit register of FPGA 5GNR FEC device */
+static uint32_t
+fpga_reg_read_32(void *mmio_base, uint32_t offset)
+{
+	void *reg_addr = mmio_base + offset;
+	uint32_t ret = *((volatile uint32_t *)(reg_addr));
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+	return __bswap_32(ret);
+#else
+	return ret;
+#endif
+}
+
+static inline void
+fpga_reg_write_16(void *mmio_base, uint32_t offset,
+		uint16_t payload) {
+	void *reg_addr = mmio_base + offset;
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+	payload = __bswap_16(payload);
+#endif
+	*((volatile uint16_t *) (reg_addr)) = payload;
+}
+
+static inline void
+fpga_reg_write_32(void *mmio_base, uint32_t offset,
+		uint32_t payload)
+{
+	void *reg_addr = mmio_base + offset;
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+	payload = __bswap_32(payload);
+#endif
+	*((volatile uint32_t *) (reg_addr)) = payload;
+}
+
+static inline void
+set_default_fpga_conf(struct fpga_5gnr_fec_conf *def_conf)
+{
+	/* Set pf mode to true */
+	def_conf->pf_mode_en = true;
+
+	/* Set ratio between UL and DL to 1:1 (unit of weight is 3 CBs) */
+	def_conf->ul_bandwidth = 3;
+	def_conf->dl_bandwidth = 3;
+
+	/* Set Load Balance Factor to 64 */
+	def_conf->dl_load_balance = 64;
+	def_conf->ul_load_balance = 64;
+}
+
+static int
+fpga_read_config_file(const char *arg_cfg_filename,
+		struct fpga_5gnr_fec_conf *fpga_5gnr_fec_conf)
+{
+	const char *cfg_filename;
+
+	if (arg_cfg_filename == NULL) {
+		cfg_filename = getenv(FPGA_5GNR_FEC_CONFIG_FILE_ENV);
+		if (cfg_filename == NULL) {
+			cfg_filename = FPGA_5GNR_FEC_CONFIG_FILE_NAME;
+			printf("'%s' was not set. %s will be used\n",
+					FPGA_5GNR_FEC_CONFIG_FILE_NAME,
+					cfg_filename);
+		} else
+			printf("'%s=%s' config file will be used\n",
+					FPGA_5GNR_FEC_CONFIG_FILE_ENV,
+					cfg_filename);
+	} else
+		cfg_filename = arg_cfg_filename;
+
+	return fpga_5gnr_parse_conf_file(cfg_filename, fpga_5gnr_fec_conf);
+}
+
+/* Read Static Register of FPGA 5GNR FEC device */
+static inline void
+print_static_reg_debug_info(void *mmio_base)
+{
+	uint8_t i, q_id;
+	uint32_t fid;
+	uint32_t version_id = fpga_reg_read_32(mmio_base,
+			FPGA_5GNR_FEC_VERSION_ID);
+	uint16_t config = fpga_reg_read_16(mmio_base,
+			FPGA_5GNR_FEC_CONFIGURATION);
+	uint8_t qmap_done = fpga_reg_read_8(mmio_base,
+			FPGA_5GNR_FEC_QUEUE_PF_VF_MAP_DONE);
+	uint16_t lb_factor = fpga_reg_read_16(mmio_base,
+			FPGA_5GNR_FEC_LOAD_BALANCE_FACTOR);
+	uint16_t ring_desc_len = fpga_reg_read_16(mmio_base,
+			FPGA_5GNR_FEC_RING_DESC_LEN);
+	uint16_t flr_time_out = fpga_reg_read_16(mmio_base,
+			FPGA_5GNR_FEC_FLR_TIME_OUT);
+
+	printf("FEC FPGA RTL v%u.%u\n",
+		((uint16_t)(version_id >> 16)), ((uint16_t)version_id));
+	printf("UL.DL Weights = %u.%u\n",
+			((uint8_t)config), ((uint8_t)(config >> 8)));
+	printf("UL.DL Load Balance = %u.%u\n",
+			((uint8_t)lb_factor), ((uint8_t)(lb_factor >> 8)));
+	printf("Queue-PF/VF Mapping Table = %s\n",
+			(qmap_done > 0) ? "READY" : "NOT-READY");
+	printf("Ring Descriptor Size = %u bytes\n",
+			ring_desc_len*FPGA_RING_DESC_LEN_UNIT_BYTES);
+	printf("FLR Timeout = %f usec\n",
+			(float)flr_time_out*FPGA_FLR_TIMEOUT_UNIT);
+
+	printf("\n--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n");
+	printf("        |  PF | VF0 | VF1 | VF2 | VF3 | VF4 | VF5 | VF6 | VF7 |\n");
+	printf("--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n");
+
+	for (q_id = 0; q_id < FPGA_TOTAL_NUM_QUEUES; q_id++) {
+
+		printf("%s-Q'%02u |",
+			(q_id < FPGA_NUM_UL_QUEUES) ? "UL" : "DL", q_id);
+
+		fid = fpga_reg_read_32(mmio_base,
+				FPGA_5GNR_FEC_QUEUE_MAP + (q_id << 2));
+
+		for (i = 0; i < 9; ++i) {
+
+			if (!((fid >> 16) & (0x80)) && i == 0) {
+				printf("  X  |");
+				continue;
+			}
+
+			if (((((fid >> 16) & (0x7f)) + 1) == i) &&
+					((fid >> 16) & (0x80)))
+				printf("  X  |");
+			else
+				printf("     |");
+		}
+		printf("\n");
+	}
+	printf("--------+-----+-----+-----+-----+-----+-----+-----+-----+-----+\n\n");
+}
+
+static int
+fpga_write_config(void *mapaddr, struct fpga_5gnr_fec_conf *conf)
+{
+
+	uint32_t payload_32, address;
+	uint16_t payload_16;
+	uint16_t q_id, vf_id, total_q_id, total_ul_q_id, total_dl_q_id;
+
+	uint32_t *bar0addr = mapaddr;
+
+	/*
+	 * Configure UL:DL ratio.
+	 * [7:0]: UL weight
+	 * [15:8]: DL weight
+	 */
+	payload_16 = (conf->dl_bandwidth << 8) | conf->ul_bandwidth;
+	address = FPGA_5GNR_FEC_CONFIGURATION;
+	fpga_reg_write_16(bar0addr, address, payload_16);
+
+	/* Clear all queues registers */
+	payload_32 = FPGA_INVALID_HW_QUEUE_ID;
+	for (q_id = 0; q_id < FPGA_TOTAL_NUM_QUEUES; ++q_id) {
+		address = (q_id << 2) + FPGA_5GNR_FEC_QUEUE_MAP;
+		fpga_reg_write_32(bar0addr, address, payload_32);
+	}
+
+	/*
+	 * If PF mode is enabled allocate all queues for PF only.
+	 *
+	 * For VF mode each VF can have different number of UL and DL queues.
+	 * Total number of queues to configure cannot exceed FPGA
+	 * capabilities - 64 queues - 32 queues for UL and 32 queues for DL.
+	 * Queues mapping is done according to configuration:
+	 *
+	 * UL queues:
+	 * |                Q_ID              | VF_ID |
+	 * |                 0                |   0   |
+	 * |                ...               |   0   |
+	 * | conf->vf_dl_queues_number[0] - 1 |   0   |
+	 * | conf->vf_dl_queues_number[0]     |   1   |
+	 * |                ...               |   1   |
+	 * | conf->vf_dl_queues_number[1] - 1 |   1   |
+	 * |                ...               |  ...  |
+	 * | conf->vf_dl_queues_number[7] - 1 |   7   |
+	 *
+	 * DL queues:
+	 * |                Q_ID              | VF_ID |
+	 * |                 32               |   0   |
+	 * |                ...               |   0   |
+	 * | conf->vf_ul_queues_number[0] - 1 |   0   |
+	 * | conf->vf_ul_queues_number[0]     |   1   |
+	 * |                ...               |   1   |
+	 * | conf->vf_ul_queues_number[1] - 1 |   1   |
+	 * |                ...               |  ...  |
+	 * | conf->vf_ul_queues_number[7] - 1 |   7   |
+	 *
+	 * Example of configuration:
+	 * conf->vf_ul_queues_number[0] = 4;  -> 4 UL queues for VF0
+	 * conf->vf_dl_queues_number[0] = 4;  -> 4 DL queues for VF0
+	 * conf->vf_ul_queues_number[1] = 2;  -> 2 UL queues for VF1
+	 * conf->vf_dl_queues_number[1] = 2;  -> 2 DL queues for VF1
+	 *
+	 * UL:
+	 * | Q_ID | VF_ID |
+	 * |   0  |   0   |
+	 * |   1  |   0   |
+	 * |   2  |   0   |
+	 * |   3  |   0   |
+	 * |   4  |   1   |
+	 * |   5  |   1   |
+	 *
+	 * DL:
+	 * | Q_ID | VF_ID |
+	 * |  32  |   0   |
+	 * |  33  |   0   |
+	 * |  34  |   0   |
+	 * |  35  |   0   |
+	 * |  36  |   1   |
+	 * |  37  |   1   |
+	 */
+	if (conf->pf_mode_en) {
+		payload_32 = 0x1;
+		for (q_id = 0; q_id < FPGA_TOTAL_NUM_QUEUES; ++q_id) {
+			address = (q_id << 2) + FPGA_5GNR_FEC_QUEUE_MAP;
+			fpga_reg_write_32(bar0addr, address, payload_32);
+		}
+	} else {
+		/* Calculate total number of UL and DL queues to configure */
+		total_ul_q_id = total_dl_q_id = 0;
+		for (vf_id = 0; vf_id < FPGA_5GNR_FEC_NUM_VFS; ++vf_id) {
+			total_ul_q_id += conf->vf_ul_queues_number[vf_id];
+			total_dl_q_id += conf->vf_dl_queues_number[vf_id];
+		}
+		total_q_id = total_dl_q_id + total_ul_q_id;
+		/*
+		 * Check if total number of queues to configure does not exceed
+		 * FPGA capabilities (64 queues - 32 UL and 32 DL queues)
+		 */
+		if ((total_ul_q_id > FPGA_NUM_UL_QUEUES) ||
+			(total_dl_q_id > FPGA_NUM_DL_QUEUES) ||
+			(total_q_id > FPGA_TOTAL_NUM_QUEUES)) {
+			printf(
+					"FPGA Configuration failed. Too many queues to configure: UL_Q %u, DL_Q %u, FPGA_Q %u",
+					total_ul_q_id, total_dl_q_id,
+					FPGA_TOTAL_NUM_QUEUES);
+			return -EINVAL;
+		}
+		total_ul_q_id = 0;
+		for (vf_id = 0; vf_id < FPGA_5GNR_FEC_NUM_VFS; ++vf_id) {
+			for (q_id = 0; q_id < conf->vf_ul_queues_number[vf_id];
+					++q_id, ++total_ul_q_id) {
+				address = (total_ul_q_id << 2) +
+						FPGA_5GNR_FEC_QUEUE_MAP;
+				payload_32 = ((0x80 + vf_id) << 16) | 0x1;
+				fpga_reg_write_32(bar0addr, address,
+						payload_32);
+			}
+		}
+		total_dl_q_id = 0;
+		for (vf_id = 0; vf_id < FPGA_5GNR_FEC_NUM_VFS; ++vf_id) {
+			for (q_id = 0; q_id < conf->vf_dl_queues_number[vf_id];
+					++q_id, ++total_dl_q_id) {
+				address = ((total_dl_q_id + FPGA_NUM_UL_QUEUES)
+						<< 2) + FPGA_5GNR_FEC_QUEUE_MAP;
+				payload_32 = ((0x80 + vf_id) << 16) | 0x1;
+				fpga_reg_write_32(bar0addr, address,
+						payload_32);
+			}
+		}
+	}
+
+	/* Setting Load Balance Factor */
+	payload_16 = (conf->dl_load_balance << 8) | (conf->ul_load_balance);
+	address = FPGA_5GNR_FEC_LOAD_BALANCE_FACTOR;
+	fpga_reg_write_16(bar0addr, address, payload_16);
+
+	/* Setting length of ring descriptor entry */
+	payload_16 = FPGA_RING_DESC_ENTRY_LENGTH;
+	address = FPGA_5GNR_FEC_RING_DESC_LEN;
+	fpga_reg_write_16(bar0addr, address, payload_16);
+
+	/* Setting FLR timeout value */
+	payload_16 = conf->flr_time_out;
+	address = FPGA_5GNR_FEC_FLR_TIME_OUT;
+	fpga_reg_write_16(bar0addr, address, payload_16);
+
+	/* Queue PF/VF mapping table is ready */
+	payload_16 = 0x1;
+	address = FPGA_5GNR_FEC_QUEUE_PF_VF_MAP_DONE;
+	fpga_reg_write_16(bar0addr, address, payload_16);
+
+	print_static_reg_debug_info(bar0addr);
+	printf("Mode of operation = %s-mode\n",
+			conf->pf_mode_en ? "PF" : "VF");
+
+	return 0;
+}
+
+int
+fpga_5gnr_configure(void *bar0addr, const char *cfg_filename)
+{
+	struct fpga_5gnr_fec_conf fpga_conf;
+	int ret;
+
+	ret = fpga_read_config_file(cfg_filename, &fpga_conf);
+	if (ret != 0) {
+		printf("Error reading config file.\n");
+		return -1;
+	}
+
+	ret = fpga_write_config(bar0addr, &fpga_conf);
+	if (ret != 0) {
+		printf("Error writing configuration for FPGA.\n");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.h b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.h
new file mode 100644
index 0000000..f7ab8e1
--- /dev/null
+++ b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_app.h
@@ -0,0 +1,102 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#ifndef _FPGA_5GNR_CFG_APP_H_
+#define _FPGA_5GNR_CFG_APP_H_
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <errno.h>
+
+/**< Number of Virtual Functions FGPA 5GNR FEC supports */
+#define FPGA_5GNR_FEC_NUM_VFS 8
+#define FPGA_5GNR_FEC_VENDOR_ID 0x8086
+#define FPGA_5GNR_FEC_DEVICE_ID 0x0D8F
+#define FPGA_5GNR_FEC_BAR_SIZE  0x1000
+#define FPGA_5GNR_FEC_DEV_NAME  "FPGA_5GNR_FEC"
+#define FPGA_5GNR_FEC_CONFIG_FILE_ENV "FPGA_5GNR_FEC_CONFIG_FILE"
+#define FPGA_5GNR_FEC_CONFIG_FILE_NAME "fpga_5gnr_fec_config.cfg"
+
+/* Multiplier of 256 bits (32 bytes) */
+#define FPGA_RING_DESC_ENTRY_LENGTH (8)
+#define FPGA_RING_DESC_LEN_UNIT_BYTES (32)
+/* Maximum size of queue */
+#define FPGA_RING_MAX_SIZE (1024)
+#define FPGA_FLR_TIMEOUT_UNIT (16.384)
+
+#define FPGA_NUM_UL_QUEUES (32)
+#define FPGA_NUM_DL_QUEUES (32)
+#define FPGA_TOTAL_NUM_QUEUES (FPGA_NUM_UL_QUEUES + FPGA_NUM_DL_QUEUES)
+
+#define FPGA_INVALID_HW_QUEUE_ID (0xFFFFFFFF)
+
+/* FPGA 5GNR FEC Register mapping on BAR0 */
+enum {
+	FPGA_5GNR_FEC_VERSION_ID = 0x00000000, /* len: 4B */
+	FPGA_5GNR_FEC_CONFIGURATION = 0x00000004, /* len: 2B */
+	FPGA_5GNR_FEC_QUEUE_PF_VF_MAP_DONE = 0x00000008, /* len: 1B */
+	FPGA_5GNR_FEC_LOAD_BALANCE_FACTOR = 0x0000000a, /* len: 2B */
+	FPGA_5GNR_FEC_RING_DESC_LEN = 0x0000000c, /* len: 2B */
+	FPGA_5GNR_FEC_FLR_TIME_OUT = 0x0000000e, /* len: 2B */
+	FPGA_5GNR_FEC_VFQ_FLUSH_STATUS_LW = 0x00000018, /* len: 4B */
+	FPGA_5GNR_FEC_VFQ_FLUSH_STATUS_HI = 0x0000001c, /* len: 4B */
+	FPGA_5GNR_FEC_QUEUE_MAP = 0x00000040, /* len: 256B */
+	FPGA_5GNR_FEC_RING_CTRL_REGS = 0x00000200, /* len: 2048B */
+	FPGA_5GNR_FEC_DDR4_WR_ADDR_REGS = 0x00000A00, /* len: 4B */
+	FPGA_5GNR_FEC_DDR4_WR_DATA_REGS = 0x00000A08, /* len: 8B */
+	FPGA_5GNR_FEC_DDR4_WR_DONE_REGS = 0x00000A10, /* len: 1B */
+	FPGA_5GNR_FEC_DDR4_RD_ADDR_REGS = 0x00000A18, /* len: 4B */
+	FPGA_5GNR_FEC_DDR4_RD_DONE_REGS = 0x00000A20, /* len: 1B */
+	FPGA_5GNR_FEC_DDR4_RD_RDY_REGS = 0x00000A28, /* len: 1B */
+	FPGA_5GNR_FEC_DDR4_RD_DATA_REGS = 0x00000A30, /* len: 8B */
+	FPGA_5GNR_FEC_DDR4_ADDR_RDY_REGS = 0x00000A38, /* len: 1B */
+	FPGA_5GNR_FEC_HARQ_BUF_SIZE_RDY_REGS = 0x00000A40, /* len: 1B */
+	FPGA_5GNR_FEC_HARQ_BUF_SIZE_REGS = 0x00000A48, /* len: 4B */
+	FPGA_5GNR_FEC_MUTEX = 0x00000A60, /* len: 4B */
+	FPGA_5GNR_FEC_MUTEX_RESET = 0x00000A68  /* len: 4B */
+};
+
+/* FIXME Add HARQ/DDR Registers */
+
+/* FPGA 5GNR FEC Ring Control Registers */
+enum {
+	FPGA_5GNR_FEC_RING_HEAD_ADDR = 0x00000008,
+	FPGA_5GNR_FEC_RING_SIZE = 0x00000010,
+	FPGA_5GNR_FEC_RING_MISC = 0x00000014,
+	FPGA_5GNR_FEC_RING_ENABLE = 0x00000015,
+	FPGA_5GNR_FEC_RING_FLUSH_QUEUE_EN = 0x00000016,
+	FPGA_5GNR_FEC_RING_SHADOW_TAIL = 0x00000018,
+	FPGA_5GNR_FEC_RING_HEAD_POINT = 0x0000001C
+};
+
+struct
+fpga_5gnr_fec_conf {
+	/**< 1 if PF is used for dataplane, 0 for VFs */
+	bool pf_mode_en;
+	/**< Number of UL queues per VF */
+	uint8_t vf_ul_queues_number[FPGA_5GNR_FEC_NUM_VFS];
+	/**< Number of DL queues per VF */
+	uint8_t vf_dl_queues_number[FPGA_5GNR_FEC_NUM_VFS];
+	/**< UL bandwidth. Needed for schedule algorithm */
+	uint8_t ul_bandwidth;
+	/**< DL bandwidth. Needed for schedule algorithm */
+	uint8_t dl_bandwidth;
+	/**< UL Load Balance */
+	uint8_t ul_load_balance;
+	/**< DL Load Balance */
+	uint8_t dl_load_balance;
+	/**< FLR timeout value */
+	uint16_t flr_time_out;
+};
+
+/**
+ * Configure FPGA
+ */
+int fpga_5gnr_configure(void *bar0addr, const char *arg_cfg_filename);
+
+#endif /* _FPGA_5GNR_CFG_APP_H_ */
diff --git a/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_parser.c b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_parser.c
new file mode 100644
index 0000000..271b213
--- /dev/null
+++ b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_cfg_parser.c
@@ -0,0 +1,187 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2020 Intel Corporation
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include <ini.h>
+#include "fpga_5gnr_cfg_app.h"
+
+/* Names of sections used in the configuration file */
+#define MODE "MODE"
+#define UL "UL"
+#define DL "DL"
+#define FLR "FLR"
+
+/* Names of entries in sections used in the configuration file */
+#define PFMODE "pf_mode_en"
+#define BANDWIDTH "bandwidth"
+#define LOAD_BALANCE "load_balance"
+#define QUEUE_MAP "vfqmap"
+#define FLR_TIME_OUT "flr_time_out"
+
+/* Default values for FPGA device configuration variables */
+#define DEFAULT_PF_MODE_EN 1
+#define DEFAULT_UL_BANDWIDTH 3
+#define DEFAULT_DL_BANDWIDTH 3
+#define DEFAULT_UL_LOAD_BALANCE 64
+#define DEFAULT_DL_LOAD_BALANCE 64
+#define DEFAULT_NUM_VF_QUEUE 8
+
+/* Possible values for MODE and LLR_SIGN */
+#define ZERO "0"
+#define ONE "1"
+
+static int
+parse_number8(const char *str, uint8_t *value)
+{
+	uint64_t val = 0;
+	char *end;
+
+	if (str == NULL)
+		return -EINVAL;
+
+	val = strtoul(str, &end, 0);
+	if (val > UINT8_MAX || str == end) {
+		printf("ERROR: Invalid value %" PRIu64 "\n", val);
+		return -ERANGE;
+	}
+
+	*value = (uint8_t) val;
+	return 1;
+}
+
+static int
+parse_number16(const char *str, uint16_t *value)
+{
+	uint64_t val = 0;
+	char *end;
+
+	if (str == NULL)
+		return -EINVAL;
+
+	val = strtoul(str, &end, 0);
+	if (val > UINT16_MAX || str == end) {
+		printf("ERROR: Invalid value %" PRIu64 "\n", val);
+		return -ERANGE;
+	}
+
+	*value = (uint16_t) val;
+	return 1;
+}
+
+static int
+parse_array8(const char *str, uint8_t *array)
+{
+	int i;
+	uint64_t val = 0;
+	char *end;
+
+	if (str == NULL)
+		return -EINVAL;
+
+	char *val_ch = strtok((char *)str, ",");
+	for (i = 0; i < 8 && NULL != val_ch; i++) {
+
+		val = strtoul(val_ch, &end, 0);
+		if (val > UINT8_MAX || val_ch == end) {
+			printf("ERROR: Invalid value %" PRIu64 "\n", val);
+			return -ERANGE;
+		}
+		array[i] = (uint8_t) val;
+
+		val_ch = strtok(NULL, ",");
+	}
+	return 1;
+}
+
+static void
+set_default_config(struct fpga_5gnr_fec_conf *fpga_conf)
+{
+	int i;
+
+	/* Set pf mode to true */
+	fpga_conf->pf_mode_en = DEFAULT_PF_MODE_EN;
+
+	/* Set ratio between UL and DL to 1:1 (unit of weight is 3 CBs) */
+	fpga_conf->ul_bandwidth = DEFAULT_UL_BANDWIDTH;
+	fpga_conf->dl_bandwidth = DEFAULT_DL_BANDWIDTH;
+
+	/* Set Load Balance Factor to 64 */
+	fpga_conf->ul_load_balance = DEFAULT_UL_LOAD_BALANCE;
+	fpga_conf->dl_load_balance = DEFAULT_DL_LOAD_BALANCE;
+
+	for (i = 0; i < FPGA_5GNR_FEC_NUM_VFS; i++) {
+		fpga_conf->vf_ul_queues_number[i] = DEFAULT_NUM_VF_QUEUE / 2;
+		fpga_conf->vf_dl_queues_number[i] = DEFAULT_NUM_VF_QUEUE / 2;
+	}
+}
+
+static int
+fpga_handler(void *user, const char *section,
+	     const char *name, const char *value)
+{
+	struct fpga_5gnr_fec_conf *fpga_conf =
+			(struct fpga_5gnr_fec_conf *) user;
+	int ret = 1;
+
+	if (!strcmp(section, MODE) && !strcmp(name, PFMODE)) {
+		if (!strcmp(value, ZERO))
+			fpga_conf->pf_mode_en = false;
+		else if (!strcmp(value, ONE))
+			fpga_conf->pf_mode_en = true;
+		else
+			ret = 0;
+	} else if (!strcmp(section, UL) && !strcmp(name, BANDWIDTH)) {
+		ret = parse_number8(value, &fpga_conf->ul_bandwidth);
+	} else if (!strcmp(section, DL) && !strcmp(name, BANDWIDTH)) {
+		ret = parse_number8(value, &fpga_conf->dl_bandwidth);
+	} else if (!strcmp(section, UL) && !strcmp(name, LOAD_BALANCE)) {
+		ret = parse_number8(value, &fpga_conf->ul_load_balance);
+	} else if (!strcmp(section, DL) && !strcmp(name, LOAD_BALANCE)) {
+		ret = parse_number8(value, &fpga_conf->dl_load_balance);
+	} else if (!strcmp(section, UL) && !strcmp(name, QUEUE_MAP)) {
+		ret = parse_array8(value, fpga_conf->vf_ul_queues_number);
+	} else if (!strcmp(section, DL) && !strcmp(name, QUEUE_MAP)) {
+		ret = parse_array8(value, fpga_conf->vf_dl_queues_number);
+	} else if (!strcmp(section, FLR) && !strcmp(name, FLR_TIME_OUT)) {
+		ret = parse_number16(value, &fpga_conf->flr_time_out);
+	} else {
+		printf("ERROR: Section (%s) or name (%s) is not valid.\n",
+		       section, name);
+		return 0;
+	}
+	if (ret != 1)
+		printf("Error: Conversion of value (%s) failed.\n", value);
+
+	return ret;
+}
+
+int
+fpga_5gnr_parse_conf_file(const char *file_name,
+		struct fpga_5gnr_fec_conf *fpga_conf)
+{
+	int ret;
+
+	set_default_config(fpga_conf);
+
+	ret = ini_parse(file_name, fpga_handler, fpga_conf);
+
+	if (ret == -1) {
+		printf("ERROR: Error loading configuration file %s\n",
+			file_name);
+		set_default_config(fpga_conf);
+		return -ENOENT;
+	} else if (ret == -2) {
+		printf("ERROR: Memory allocation error\n");
+		set_default_config(fpga_conf);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
diff --git a/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_config.cfg b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_config.cfg
new file mode 100644
index 0000000..0854672
--- /dev/null
+++ b/drivers/baseband/fpga_5gnr_fec/pf_config_app/fpga_5gnr_config.cfg
@@ -0,0 +1,18 @@ 
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2020 Intel Corporation
+
+[MODE]
+pf_mode_en = 1
+
+[UL]
+bandwidth = 3
+load_balance = 128
+vfqmap = 4,4,4,4,4,4,4,4
+
+[DL]
+bandwidth = 3
+load_balance = 128
+vfqmap = 4,4,4,4,4,4,4,4
+
+[FLR]
+flr_time_out = 610