Contributors: 33
Author Tokens Token Proportion Commits Commit Proportion
Kishon Vijay Abraham I 2759 41.01% 14 16.47%
Frank Li 1114 16.56% 2 2.35%
Koichiro Den 844 12.54% 2 2.35%
Damien Le Moal 724 10.76% 14 16.47%
Niklas Svensson (Niklas Cassel) 598 8.89% 9 10.59%
Manivannan Sadhasivam 245 3.64% 11 12.94%
Gustavo Pimentel 131 1.95% 4 4.71%
Cyrille Pitchen 66 0.98% 1 1.18%
Vidya Sagar 46 0.68% 1 1.18%
Marek Vašut 33 0.49% 1 1.18%
Shin'ichiro Kawasaki 22 0.33% 1 1.18%
Christian Bruel 18 0.27% 1 1.18%
Kangjie Lu 18 0.27% 1 1.18%
Shradha Todi 16 0.24% 1 1.18%
Hou Zhiqiang 15 0.22% 1 1.18%
Yang Yingliang 14 0.21% 1 1.18%
Denis Efremov 13 0.19% 1 1.18%
John Keeping 10 0.15% 1 1.18%
Alan Mikhak 9 0.13% 3 3.53%
Kunihiko Hayashi 8 0.12% 1 1.18%
Bhanu Seshu Kumar Valluri 4 0.06% 1 1.18%
Jia-Ju Bai 4 0.06% 1 1.18%
Kees Cook 3 0.04% 1 1.18%
Björn Helgaas 2 0.03% 2 2.35%
Marco Crivellari 2 0.03% 1 1.18%
Wen Yang 2 0.03% 1 1.18%
Andy Shevchenko 2 0.03% 1 1.18%
Krzysztof Kozlowski 1 0.01% 1 1.18%
Mohamed Khalfella 1 0.01% 1 1.18%
Krzysztof Wilczynski 1 0.01% 1 1.18%
Niklas Cassel 1 0.01% 1 1.18%
Lars-Peter Clausen 1 0.01% 1 1.18%
Yoshihiro Shimoda 1 0.01% 1 1.18%
Total 6728 85


// SPDX-License-Identifier: GPL-2.0
/*
 * Test driver to test endpoint functionality
 *
 * Copyright (C) 2017 Texas Instruments
 * Author: Kishon Vijay Abraham I <kishon@ti.com>
 */

#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
#include <linux/random.h>

#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
#include <linux/pci-ep-msi.h>
#include <linux/pci_regs.h>

#define IRQ_TYPE_INTX			0
#define IRQ_TYPE_MSI			1
#define IRQ_TYPE_MSIX			2

#define COMMAND_RAISE_INTX_IRQ		BIT(0)
#define COMMAND_RAISE_MSI_IRQ		BIT(1)
#define COMMAND_RAISE_MSIX_IRQ		BIT(2)
#define COMMAND_READ			BIT(3)
#define COMMAND_WRITE			BIT(4)
#define COMMAND_COPY			BIT(5)
#define COMMAND_ENABLE_DOORBELL		BIT(6)
#define COMMAND_DISABLE_DOORBELL	BIT(7)
#define COMMAND_BAR_SUBRANGE_SETUP	BIT(8)
#define COMMAND_BAR_SUBRANGE_CLEAR	BIT(9)

#define STATUS_READ_SUCCESS		BIT(0)
#define STATUS_READ_FAIL		BIT(1)
#define STATUS_WRITE_SUCCESS		BIT(2)
#define STATUS_WRITE_FAIL		BIT(3)
#define STATUS_COPY_SUCCESS		BIT(4)
#define STATUS_COPY_FAIL		BIT(5)
#define STATUS_IRQ_RAISED		BIT(6)
#define STATUS_SRC_ADDR_INVALID		BIT(7)
#define STATUS_DST_ADDR_INVALID		BIT(8)
#define STATUS_DOORBELL_SUCCESS		BIT(9)
#define STATUS_DOORBELL_ENABLE_SUCCESS	BIT(10)
#define STATUS_DOORBELL_ENABLE_FAIL	BIT(11)
#define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12)
#define STATUS_DOORBELL_DISABLE_FAIL	BIT(13)
#define STATUS_BAR_SUBRANGE_SETUP_SUCCESS	BIT(14)
#define STATUS_BAR_SUBRANGE_SETUP_FAIL		BIT(15)
#define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS	BIT(16)
#define STATUS_BAR_SUBRANGE_CLEAR_FAIL		BIT(17)

#define FLAG_USE_DMA			BIT(0)

#define TIMER_RESOLUTION		1

#define CAP_UNALIGNED_ACCESS		BIT(0)
#define CAP_MSI				BIT(1)
#define CAP_MSIX			BIT(2)
#define CAP_INTX			BIT(3)
#define CAP_SUBRANGE_MAPPING		BIT(4)

#define PCI_EPF_TEST_BAR_SUBRANGE_NSUB	2

static struct workqueue_struct *kpcitest_workqueue;

struct pci_epf_test {
	void			*reg[PCI_STD_NUM_BARS];
	struct pci_epf		*epf;
	struct config_group	group;
	enum pci_barno		test_reg_bar;
	size_t			msix_table_offset;
	struct delayed_work	cmd_handler;
	struct dma_chan		*dma_chan_tx;
	struct dma_chan		*dma_chan_rx;
	struct dma_chan		*transfer_chan;
	dma_cookie_t		transfer_cookie;
	enum dma_status		transfer_status;
	struct completion	transfer_complete;
	bool			dma_supported;
	bool			dma_private;
	const struct pci_epc_features *epc_features;
	struct pci_epf_bar	db_bar;
	size_t			bar_size[PCI_STD_NUM_BARS];
};

struct pci_epf_test_reg {
	__le32 magic;
	__le32 command;
	__le32 status;
	__le64 src_addr;
	__le64 dst_addr;
	__le32 size;
	__le32 checksum;
	__le32 irq_type;
	__le32 irq_number;
	__le32 flags;
	__le32 caps;
	__le32 doorbell_bar;
	__le32 doorbell_offset;
	__le32 doorbell_data;
} __packed;

static struct pci_epf_header test_header = {
	.vendorid	= PCI_ANY_ID,
	.deviceid	= PCI_ANY_ID,
	.baseclass_code = PCI_CLASS_OTHERS,
	.interrupt_pin	= PCI_INTERRUPT_INTA,
};

/* default BAR sizes, can be overridden by the user using configfs */
static size_t default_bar_size[] = { 131072, 131072, 131072, 131072, 131072, 1048576 };

static void pci_epf_test_dma_callback(void *param)
{
	struct pci_epf_test *epf_test = param;
	struct dma_tx_state state;

	epf_test->transfer_status =
		dmaengine_tx_status(epf_test->transfer_chan,
				    epf_test->transfer_cookie, &state);
	if (epf_test->transfer_status == DMA_COMPLETE ||
	    epf_test->transfer_status == DMA_ERROR)
		complete(&epf_test->transfer_complete);
}

/**
 * pci_epf_test_data_transfer() - Function that uses dmaengine API to transfer
 *				  data between PCIe EP and remote PCIe RC
 * @epf_test: the EPF test device that performs the data transfer operation
 * @dma_dst: The destination address of the data transfer. It can be a physical
 *	     address given by pci_epc_mem_alloc_addr or DMA mapping APIs.
 * @dma_src: The source address of the data transfer. It can be a physical
 *	     address given by pci_epc_mem_alloc_addr or DMA mapping APIs.
 * @len: The size of the data transfer
 * @dma_remote: remote RC physical address
 * @dir: DMA transfer direction
 *
 * Function that uses dmaengine API to transfer data between PCIe EP and remote
 * PCIe RC. The source and destination address can be a physical address given
 * by pci_epc_mem_alloc_addr or the one obtained using DMA mapping APIs.
 *
 * The function returns '0' on success and negative value on failure.
 */
static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test,
				      dma_addr_t dma_dst, dma_addr_t dma_src,
				      size_t len, dma_addr_t dma_remote,
				      enum dma_transfer_direction dir)
{
	struct dma_chan *chan = (dir == DMA_MEM_TO_DEV) ?
				 epf_test->dma_chan_tx : epf_test->dma_chan_rx;
	dma_addr_t dma_local = (dir == DMA_MEM_TO_DEV) ? dma_src : dma_dst;
	enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
	struct pci_epf *epf = epf_test->epf;
	struct dma_async_tx_descriptor *tx;
	struct dma_slave_config sconf = {};
	struct device *dev = &epf->dev;
	int ret;

	if (IS_ERR_OR_NULL(chan)) {
		dev_err(dev, "Invalid DMA memcpy channel\n");
		return -EINVAL;
	}

	if (epf_test->dma_private) {
		sconf.direction = dir;
		if (dir == DMA_MEM_TO_DEV)
			sconf.dst_addr = dma_remote;
		else
			sconf.src_addr = dma_remote;

		if (dmaengine_slave_config(chan, &sconf)) {
			dev_err(dev, "DMA slave config fail\n");
			return -EIO;
		}
		tx = dmaengine_prep_slave_single(chan, dma_local, len, dir,
						 flags);
	} else {
		tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len,
					       flags);
	}

	if (!tx) {
		dev_err(dev, "Failed to prepare DMA memcpy\n");
		return -EIO;
	}

	reinit_completion(&epf_test->transfer_complete);
	epf_test->transfer_chan = chan;
	tx->callback = pci_epf_test_dma_callback;
	tx->callback_param = epf_test;
	epf_test->transfer_cookie = dmaengine_submit(tx);

	ret = dma_submit_error(epf_test->transfer_cookie);
	if (ret) {
		dev_err(dev, "Failed to do DMA tx_submit %d\n", ret);
		goto terminate;
	}

	dma_async_issue_pending(chan);
	ret = wait_for_completion_interruptible(&epf_test->transfer_complete);
	if (ret < 0) {
		dev_err(dev, "DMA wait_for_completion interrupted\n");
		goto terminate;
	}

	if (epf_test->transfer_status == DMA_ERROR) {
		dev_err(dev, "DMA transfer failed\n");
		ret = -EIO;
	}

terminate:
	dmaengine_terminate_sync(chan);

	return ret;
}

struct epf_dma_filter {
	struct device *dev;
	u32 dma_mask;
};

static bool epf_dma_filter_fn(struct dma_chan *chan, void *node)
{
	struct epf_dma_filter *filter = node;
	struct dma_slave_caps caps;

	memset(&caps, 0, sizeof(caps));
	dma_get_slave_caps(chan, &caps);

	return chan->device->dev == filter->dev
		&& (filter->dma_mask & caps.directions);
}

/**
 * pci_epf_test_init_dma_chan() - Function to initialize EPF test DMA channel
 * @epf_test: the EPF test device that performs data transfer operation
 *
 * Function to initialize EPF test DMA channel.
 */
static int pci_epf_test_init_dma_chan(struct pci_epf_test *epf_test)
{
	struct pci_epf *epf = epf_test->epf;
	struct device *dev = &epf->dev;
	struct epf_dma_filter filter;
	struct dma_chan *dma_chan;
	dma_cap_mask_t mask;
	int ret;

	filter.dev = epf->epc->dev.parent;
	filter.dma_mask = BIT(DMA_DEV_TO_MEM);

	dma_cap_zero(mask);
	dma_cap_set(DMA_SLAVE, mask);
	dma_chan = dma_request_channel(mask, epf_dma_filter_fn, &filter);
	if (!dma_chan) {
		dev_info(dev, "Failed to get private DMA rx channel. Falling back to generic one\n");
		goto fail_back_tx;
	}

	epf_test->dma_chan_rx = dma_chan;

	filter.dma_mask = BIT(DMA_MEM_TO_DEV);
	dma_chan = dma_request_channel(mask, epf_dma_filter_fn, &filter);

	if (!dma_chan) {
		dev_info(dev, "Failed to get private DMA tx channel. Falling back to generic one\n");
		goto fail_back_rx;
	}

	epf_test->dma_chan_tx = dma_chan;
	epf_test->dma_private = true;

	init_completion(&epf_test->transfer_complete);

	return 0;

fail_back_rx:
	dma_release_channel(epf_test->dma_chan_rx);
	epf_test->dma_chan_rx = NULL;

fail_back_tx:
	dma_cap_zero(mask);
	dma_cap_set(DMA_MEMCPY, mask);

	dma_chan = dma_request_chan_by_mask(&mask);
	if (IS_ERR(dma_chan)) {
		ret = PTR_ERR(dma_chan);
		if (ret != -EPROBE_DEFER)
			dev_err(dev, "Failed to get DMA channel\n");
		return ret;
	}
	init_completion(&epf_test->transfer_complete);

	epf_test->dma_chan_tx = epf_test->dma_chan_rx = dma_chan;

	return 0;
}

/**
 * pci_epf_test_clean_dma_chan() - Function to cleanup EPF test DMA channel
 * @epf_test: the EPF test device that performs data transfer operation
 *
 * Helper to cleanup EPF test DMA channel.
 */
static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test)
{
	if (!epf_test->dma_supported)
		return;

	if (epf_test->dma_chan_tx) {
		dma_release_channel(epf_test->dma_chan_tx);
		if (epf_test->dma_chan_tx == epf_test->dma_chan_rx) {
			epf_test->dma_chan_tx = NULL;
			epf_test->dma_chan_rx = NULL;
			return;
		}
		epf_test->dma_chan_tx = NULL;
	}

	if (epf_test->dma_chan_rx) {
		dma_release_channel(epf_test->dma_chan_rx);
		epf_test->dma_chan_rx = NULL;
	}
}

static void pci_epf_test_print_rate(struct pci_epf_test *epf_test,
				    const char *op, u64 size,
				    struct timespec64 *start,
				    struct timespec64 *end, bool dma)
{
	struct timespec64 ts = timespec64_sub(*end, *start);
	u64 rate = 0, ns;

	/* calculate the rate */
	ns = timespec64_to_ns(&ts);
	if (ns)
		rate = div64_u64(size * NSEC_PER_SEC, ns * 1000);

	dev_info(&epf_test->epf->dev,
		 "%s => Size: %llu B, DMA: %s, Time: %ptSp s, Rate: %llu KB/s\n",
		 op, size, dma ? "YES" : "NO", &ts, rate);
}

static void pci_epf_test_copy(struct pci_epf_test *epf_test,
			      struct pci_epf_test_reg *reg)
{
	int ret = 0;
	struct timespec64 start, end;
	struct pci_epf *epf = epf_test->epf;
	struct pci_epc *epc = epf->epc;
	struct device *dev = &epf->dev;
	struct pci_epc_map src_map, dst_map;
	u64 src_addr = le64_to_cpu(reg->src_addr);
	u64 dst_addr = le64_to_cpu(reg->dst_addr);
	size_t orig_size, copy_size;
	ssize_t map_size = 0;
	u32 flags = le32_to_cpu(reg->flags);
	u32 status = 0;
	void *copy_buf = NULL, *buf;

	orig_size = copy_size = le32_to_cpu(reg->size);

	if (flags & FLAG_USE_DMA) {
		if (!dma_has_cap(DMA_MEMCPY, epf_test->dma_chan_tx->device->cap_mask)) {
			dev_err(dev, "DMA controller doesn't support MEMCPY\n");
			ret = -EINVAL;
			goto set_status;
		}
	} else {
		copy_buf = kzalloc(copy_size, GFP_KERNEL);
		if (!copy_buf) {
			ret = -ENOMEM;
			goto set_status;
		}
		buf = copy_buf;
	}

	while (copy_size) {
		ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
				      src_addr, copy_size, &src_map);
		if (ret) {
			dev_err(dev, "Failed to map source address\n");
			status = STATUS_SRC_ADDR_INVALID;
			goto free_buf;
		}

		ret = pci_epc_mem_map(epf->epc, epf->func_no, epf->vfunc_no,
					   dst_addr, copy_size, &dst_map);
		if (ret) {
			dev_err(dev, "Failed to map destination address\n");
			status = STATUS_DST_ADDR_INVALID;
			pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no,
					  &src_map);
			goto free_buf;
		}

		map_size = min_t(size_t, dst_map.pci_size, src_map.pci_size);

		ktime_get_ts64(&start);
		if (flags & FLAG_USE_DMA) {
			ret = pci_epf_test_data_transfer(epf_test,
					dst_map.phys_addr, src_map.phys_addr,
					map_size, 0, DMA_MEM_TO_MEM);
			if (ret) {
				dev_err(dev, "Data transfer failed\n");
				goto unmap;
			}
		} else {
			memcpy_fromio(buf, src_map.virt_addr, map_size);
			memcpy_toio(dst_map.virt_addr, buf, map_size);
			buf += map_size;
		}
		ktime_get_ts64(&end);

		copy_size -= map_size;
		src_addr += map_size;
		dst_addr += map_size;

		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map);
		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map);
		map_size = 0;
	}

	pci_epf_test_print_rate(epf_test, "COPY", orig_size, &start, &end,
				flags & FLAG_USE_DMA);

unmap:
	if (map_size) {
		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map);
		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map);
	}

free_buf:
	kfree(copy_buf);

set_status:
	if (!ret)
		status |= STATUS_COPY_SUCCESS;
	else
		status |= STATUS_COPY_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_read(struct pci_epf_test *epf_test,
			      struct pci_epf_test_reg *reg)
{
	int ret = 0;
	void *src_buf, *buf;
	u32 crc32;
	struct pci_epc_map map;
	phys_addr_t dst_phys_addr;
	struct timespec64 start, end;
	struct pci_epf *epf = epf_test->epf;
	struct pci_epc *epc = epf->epc;
	struct device *dev = &epf->dev;
	struct device *dma_dev = epf->epc->dev.parent;
	u64 src_addr = le64_to_cpu(reg->src_addr);
	size_t orig_size, src_size;
	ssize_t map_size = 0;
	u32 flags = le32_to_cpu(reg->flags);
	u32 checksum = le32_to_cpu(reg->checksum);
	u32 status = 0;

	orig_size = src_size = le32_to_cpu(reg->size);

	src_buf = kzalloc(src_size, GFP_KERNEL);
	if (!src_buf) {
		ret = -ENOMEM;
		goto set_status;
	}
	buf = src_buf;

	while (src_size) {
		ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
					   src_addr, src_size, &map);
		if (ret) {
			dev_err(dev, "Failed to map address\n");
			status = STATUS_SRC_ADDR_INVALID;
			goto free_buf;
		}

		map_size = map.pci_size;
		if (flags & FLAG_USE_DMA) {
			dst_phys_addr = dma_map_single(dma_dev, buf, map_size,
						       DMA_FROM_DEVICE);
			if (dma_mapping_error(dma_dev, dst_phys_addr)) {
				dev_err(dev,
					"Failed to map destination buffer addr\n");
				ret = -ENOMEM;
				goto unmap;
			}

			ktime_get_ts64(&start);
			ret = pci_epf_test_data_transfer(epf_test,
					dst_phys_addr, map.phys_addr,
					map_size, src_addr, DMA_DEV_TO_MEM);
			if (ret)
				dev_err(dev, "Data transfer failed\n");
			ktime_get_ts64(&end);

			dma_unmap_single(dma_dev, dst_phys_addr, map_size,
					 DMA_FROM_DEVICE);

			if (ret)
				goto unmap;
		} else {
			ktime_get_ts64(&start);
			memcpy_fromio(buf, map.virt_addr, map_size);
			ktime_get_ts64(&end);
		}

		src_size -= map_size;
		src_addr += map_size;
		buf += map_size;

		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
		map_size = 0;
	}

	pci_epf_test_print_rate(epf_test, "READ", orig_size, &start, &end,
				flags & FLAG_USE_DMA);

	crc32 = crc32_le(~0, src_buf, orig_size);
	if (crc32 != checksum)
		ret = -EIO;

unmap:
	if (map_size)
		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);

free_buf:
	kfree(src_buf);

set_status:
	if (!ret)
		status |= STATUS_READ_SUCCESS;
	else
		status |= STATUS_READ_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_write(struct pci_epf_test *epf_test,
			       struct pci_epf_test_reg *reg)
{
	int ret = 0;
	void *dst_buf, *buf;
	struct pci_epc_map map;
	phys_addr_t src_phys_addr;
	struct timespec64 start, end;
	struct pci_epf *epf = epf_test->epf;
	struct pci_epc *epc = epf->epc;
	struct device *dev = &epf->dev;
	struct device *dma_dev = epf->epc->dev.parent;
	u64 dst_addr = le64_to_cpu(reg->dst_addr);
	size_t orig_size, dst_size;
	ssize_t map_size = 0;
	u32 flags = le32_to_cpu(reg->flags);
	u32 status = 0;

	orig_size = dst_size = le32_to_cpu(reg->size);

	dst_buf = kzalloc(dst_size, GFP_KERNEL);
	if (!dst_buf) {
		ret = -ENOMEM;
		goto set_status;
	}
	get_random_bytes(dst_buf, dst_size);
	reg->checksum = cpu_to_le32(crc32_le(~0, dst_buf, dst_size));
	buf = dst_buf;

	while (dst_size) {
		ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
					   dst_addr, dst_size, &map);
		if (ret) {
			dev_err(dev, "Failed to map address\n");
			status = STATUS_DST_ADDR_INVALID;
			goto free_buf;
		}

		map_size = map.pci_size;
		if (flags & FLAG_USE_DMA) {
			src_phys_addr = dma_map_single(dma_dev, buf, map_size,
						       DMA_TO_DEVICE);
			if (dma_mapping_error(dma_dev, src_phys_addr)) {
				dev_err(dev,
					"Failed to map source buffer addr\n");
				ret = -ENOMEM;
				goto unmap;
			}

			ktime_get_ts64(&start);

			ret = pci_epf_test_data_transfer(epf_test,
						map.phys_addr, src_phys_addr,
						map_size, dst_addr,
						DMA_MEM_TO_DEV);
			if (ret)
				dev_err(dev, "Data transfer failed\n");
			ktime_get_ts64(&end);

			dma_unmap_single(dma_dev, src_phys_addr, map_size,
					 DMA_TO_DEVICE);

			if (ret)
				goto unmap;
		} else {
			ktime_get_ts64(&start);
			memcpy_toio(map.virt_addr, buf, map_size);
			ktime_get_ts64(&end);
		}

		dst_size -= map_size;
		dst_addr += map_size;
		buf += map_size;

		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
		map_size = 0;
	}

	pci_epf_test_print_rate(epf_test, "WRITE", orig_size, &start, &end,
				flags & FLAG_USE_DMA);

	/*
	 * wait 1ms inorder for the write to complete. Without this delay L3
	 * error in observed in the host system.
	 */
	usleep_range(1000, 2000);

unmap:
	if (map_size)
		pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);

free_buf:
	kfree(dst_buf);

set_status:
	if (!ret)
		status |= STATUS_WRITE_SUCCESS;
	else
		status |= STATUS_WRITE_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,
				   struct pci_epf_test_reg *reg)
{
	struct pci_epf *epf = epf_test->epf;
	struct device *dev = &epf->dev;
	struct pci_epc *epc = epf->epc;
	u32 status = le32_to_cpu(reg->status);
	u32 irq_number = le32_to_cpu(reg->irq_number);
	u32 irq_type = le32_to_cpu(reg->irq_type);
	int count;

	/*
	 * Set the status before raising the IRQ to ensure that the host sees
	 * the updated value when it gets the IRQ.
	 */
	status |= STATUS_IRQ_RAISED;
	WRITE_ONCE(reg->status, cpu_to_le32(status));

	switch (irq_type) {
	case IRQ_TYPE_INTX:
		pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no,
				  PCI_IRQ_INTX, 0);
		break;
	case IRQ_TYPE_MSI:
		count = pci_epc_get_msi(epc, epf->func_no, epf->vfunc_no);
		if (irq_number > count || count <= 0) {
			dev_err(dev, "Invalid MSI IRQ number %d / %d\n",
				irq_number, count);
			return;
		}
		pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no,
				  PCI_IRQ_MSI, irq_number);
		break;
	case IRQ_TYPE_MSIX:
		count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no);
		if (irq_number > count || count <= 0) {
			dev_err(dev, "Invalid MSI-X IRQ number %d / %d\n",
				irq_number, count);
			return;
		}
		pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no,
				  PCI_IRQ_MSIX, irq_number);
		break;
	default:
		dev_err(dev, "Failed to raise IRQ, unknown type\n");
		break;
	}
}

static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
{
	struct pci_epf_test *epf_test = data;
	enum pci_barno test_reg_bar = epf_test->test_reg_bar;
	struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
	u32 status = le32_to_cpu(reg->status);

	status |= STATUS_DOORBELL_SUCCESS;
	reg->status = cpu_to_le32(status);
	pci_epf_test_raise_irq(epf_test, reg);

	return IRQ_HANDLED;
}

static void pci_epf_test_doorbell_cleanup(struct pci_epf_test *epf_test)
{
	struct pci_epf_test_reg *reg = epf_test->reg[epf_test->test_reg_bar];
	struct pci_epf *epf = epf_test->epf;

	free_irq(epf->db_msg[0].virq, epf_test);
	reg->doorbell_bar = cpu_to_le32(NO_BAR);

	pci_epf_free_doorbell(epf);
}

static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
					 struct pci_epf_test_reg *reg)
{
	u32 status = le32_to_cpu(reg->status);
	struct pci_epf *epf = epf_test->epf;
	struct pci_epc *epc = epf->epc;
	struct msi_msg *msg;
	enum pci_barno bar;
	size_t offset;
	int ret;

	ret = pci_epf_alloc_doorbell(epf, 1);
	if (ret)
		goto set_status_err;

	msg = &epf->db_msg[0].msg;
	bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1);
	if (bar < BAR_0)
		goto err_doorbell_cleanup;

	ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
				   pci_epf_test_doorbell_handler, IRQF_ONESHOT,
				   "pci-ep-test-doorbell", epf_test);
	if (ret) {
		dev_err(&epf->dev,
			"Failed to request doorbell IRQ: %d\n",
			epf->db_msg[0].virq);
		goto err_doorbell_cleanup;
	}

	reg->doorbell_data = cpu_to_le32(msg->data);
	reg->doorbell_bar = cpu_to_le32(bar);

	msg = &epf->db_msg[0].msg;
	ret = pci_epf_align_inbound_addr(epf, bar, ((u64)msg->address_hi << 32) | msg->address_lo,
					 &epf_test->db_bar.phys_addr, &offset);

	if (ret)
		goto err_doorbell_cleanup;

	reg->doorbell_offset = cpu_to_le32(offset);

	epf_test->db_bar.barno = bar;
	epf_test->db_bar.size = epf->bar[bar].size;
	epf_test->db_bar.flags = epf->bar[bar].flags;

	ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
	if (ret)
		goto err_doorbell_cleanup;

	status |= STATUS_DOORBELL_ENABLE_SUCCESS;
	reg->status = cpu_to_le32(status);
	return;

err_doorbell_cleanup:
	pci_epf_test_doorbell_cleanup(epf_test);
set_status_err:
	status |= STATUS_DOORBELL_ENABLE_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
					  struct pci_epf_test_reg *reg)
{
	enum pci_barno bar = le32_to_cpu(reg->doorbell_bar);
	u32 status = le32_to_cpu(reg->status);
	struct pci_epf *epf = epf_test->epf;
	struct pci_epc *epc = epf->epc;
	int ret;

	if (bar < BAR_0)
		goto set_status_err;

	pci_epf_test_doorbell_cleanup(epf_test);

	/*
	 * The doorbell feature temporarily overrides the inbound translation
	 * to point to the address stored in epf_test->db_bar.phys_addr, i.e.,
	 * it calls set_bar() twice without ever calling clear_bar(), as
	 * calling clear_bar() would clear the BAR's PCI address assigned by
	 * the host. Thus, when disabling the doorbell, restore the inbound
	 * translation to point to the memory allocated for the BAR.
	 */
	ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]);
	if (ret)
		goto set_status_err;

	status |= STATUS_DOORBELL_DISABLE_SUCCESS;
	reg->status = cpu_to_le32(status);

	return;

set_status_err:
	status |= STATUS_DOORBELL_DISABLE_FAIL;
	reg->status = cpu_to_le32(status);
}

static u8 pci_epf_test_subrange_sig_byte(enum pci_barno barno,
					 unsigned int subno)
{
	return 0x50 + (barno * 8) + subno;
}

static void pci_epf_test_bar_subrange_setup(struct pci_epf_test *epf_test,
					    struct pci_epf_test_reg *reg)
{
	struct pci_epf_bar_submap *submap, *old_submap;
	struct pci_epf *epf = epf_test->epf;
	struct pci_epc *epc = epf->epc;
	struct pci_epf_bar *bar;
	unsigned int nsub = PCI_EPF_TEST_BAR_SUBRANGE_NSUB, old_nsub;
	/* reg->size carries BAR number for BAR_SUBRANGE_* commands. */
	enum pci_barno barno = le32_to_cpu(reg->size);
	u32 status = le32_to_cpu(reg->status);
	unsigned int i, phys_idx;
	size_t sub_size;
	u8 *addr;
	int ret;

	if (barno >= PCI_STD_NUM_BARS) {
		dev_err(&epf->dev, "Invalid barno: %d\n", barno);
		goto err;
	}

	/* Host side should've avoided test_reg_bar, this is a safeguard. */
	if (barno == epf_test->test_reg_bar) {
		dev_err(&epf->dev, "test_reg_bar cannot be used for subrange test\n");
		goto err;
	}

	if (!epf_test->epc_features->dynamic_inbound_mapping ||
	    !epf_test->epc_features->subrange_mapping) {
		dev_err(&epf->dev, "epc driver does not support subrange mapping\n");
		goto err;
	}

	bar = &epf->bar[barno];
	if (!bar->size || !bar->addr) {
		dev_err(&epf->dev, "bar size/addr (%zu/%p) is invalid\n",
			bar->size, bar->addr);
		goto err;
	}

	if (bar->size % nsub) {
		dev_err(&epf->dev, "BAR size %zu is not divisible by %u\n",
			bar->size, nsub);
		goto err;
	}

	sub_size = bar->size / nsub;

	submap = kzalloc_objs(*submap, nsub);
	if (!submap)
		goto err;

	for (i = 0; i < nsub; i++) {
		/* Swap the two halves so RC can verify ordering. */
		phys_idx = i ^ 1;
		submap[i].phys_addr = bar->phys_addr + (phys_idx * sub_size);
		submap[i].size = sub_size;
	}

	old_submap = bar->submap;
	old_nsub = bar->num_submap;

	bar->submap = submap;
	bar->num_submap = nsub;

	ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar);
	if (ret) {
		dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret);
		bar->submap = old_submap;
		bar->num_submap = old_nsub;
		ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar);
		if (ret)
			dev_warn(&epf->dev, "Failed to restore the original BAR mapping: %d\n",
				 ret);

		kfree(submap);
		goto err;
	}
	kfree(old_submap);

	/*
	 * Fill deterministic signatures into the physical regions that
	 * each BAR subrange maps to. RC verifies these to ensure the
	 * submap order is really applied.
	 */
	addr = (u8 *)bar->addr;
	for (i = 0; i < nsub; i++) {
		phys_idx = i ^ 1;
		memset(addr + (phys_idx * sub_size),
		       pci_epf_test_subrange_sig_byte(barno, i),
		       sub_size);
	}

	status |= STATUS_BAR_SUBRANGE_SETUP_SUCCESS;
	reg->status = cpu_to_le32(status);
	return;

err:
	status |= STATUS_BAR_SUBRANGE_SETUP_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_bar_subrange_clear(struct pci_epf_test *epf_test,
					    struct pci_epf_test_reg *reg)
{
	struct pci_epf *epf = epf_test->epf;
	struct pci_epf_bar_submap *submap;
	struct pci_epc *epc = epf->epc;
	/* reg->size carries BAR number for BAR_SUBRANGE_* commands. */
	enum pci_barno barno = le32_to_cpu(reg->size);
	u32 status = le32_to_cpu(reg->status);
	struct pci_epf_bar *bar;
	unsigned int nsub;
	int ret;

	if (barno >= PCI_STD_NUM_BARS) {
		dev_err(&epf->dev, "Invalid barno: %d\n", barno);
		goto err;
	}

	bar = &epf->bar[barno];
	submap = bar->submap;
	nsub = bar->num_submap;

	if (!submap || !nsub)
		goto err;

	bar->submap = NULL;
	bar->num_submap = 0;

	ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar);
	if (ret) {
		bar->submap = submap;
		bar->num_submap = nsub;
		dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret);
		goto err;
	}
	kfree(submap);

	status |= STATUS_BAR_SUBRANGE_CLEAR_SUCCESS;
	reg->status = cpu_to_le32(status);
	return;

err:
	status |= STATUS_BAR_SUBRANGE_CLEAR_FAIL;
	reg->status = cpu_to_le32(status);
}

static void pci_epf_test_cmd_handler(struct work_struct *work)
{
	u32 command;
	struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test,
						     cmd_handler.work);
	struct pci_epf *epf = epf_test->epf;
	struct device *dev = &epf->dev;
	enum pci_barno test_reg_bar = epf_test->test_reg_bar;
	struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
	u32 irq_type = le32_to_cpu(reg->irq_type);

	command = le32_to_cpu(READ_ONCE(reg->command));
	if (!command)
		goto reset_handler;

	WRITE_ONCE(reg->command, 0);
	WRITE_ONCE(reg->status, 0);

	if ((le32_to_cpu(READ_ONCE(reg->flags)) & FLAG_USE_DMA) &&
	    !epf_test->dma_supported) {
		dev_err(dev, "Cannot transfer data using DMA\n");
		goto reset_handler;
	}

	if (irq_type > IRQ_TYPE_MSIX) {
		dev_err(dev, "Failed to detect IRQ type\n");
		goto reset_handler;
	}

	switch (command) {
	case COMMAND_RAISE_INTX_IRQ:
	case COMMAND_RAISE_MSI_IRQ:
	case COMMAND_RAISE_MSIX_IRQ:
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_WRITE:
		pci_epf_test_write(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_READ:
		pci_epf_test_read(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_COPY:
		pci_epf_test_copy(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_ENABLE_DOORBELL:
		pci_epf_test_enable_doorbell(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_DISABLE_DOORBELL:
		pci_epf_test_disable_doorbell(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_BAR_SUBRANGE_SETUP:
		pci_epf_test_bar_subrange_setup(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	case COMMAND_BAR_SUBRANGE_CLEAR:
		pci_epf_test_bar_subrange_clear(epf_test, reg);
		pci_epf_test_raise_irq(epf_test, reg);
		break;
	default:
		dev_err(dev, "Invalid command 0x%x\n", command);
		break;
	}

reset_handler:
	queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler,
			   msecs_to_jiffies(1));
}

static int pci_epf_test_set_bar(struct pci_epf *epf)
{
	int bar, ret;
	struct pci_epc *epc = epf->epc;
	struct device *dev = &epf->dev;
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	enum pci_barno test_reg_bar = epf_test->test_reg_bar;

	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
		if (!epf_test->reg[bar])
			continue;

		ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no,
				      &epf->bar[bar]);
		if (ret) {
			pci_epf_free_space(epf, epf_test->reg[bar], bar,
					   PRIMARY_INTERFACE);
			epf_test->reg[bar] = NULL;
			dev_err(dev, "Failed to set BAR%d\n", bar);
			if (bar == test_reg_bar)
				return ret;
		}
	}

	return 0;
}

static void pci_epf_test_clear_bar(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	struct pci_epc *epc = epf->epc;
	int bar;

	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
		if (!epf_test->reg[bar])
			continue;

		pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no,
				  &epf->bar[bar]);
	}
}

static void pci_epf_test_set_capabilities(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	enum pci_barno test_reg_bar = epf_test->test_reg_bar;
	struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
	struct pci_epc *epc = epf->epc;
	u32 caps = 0;

	if (epc->ops->align_addr)
		caps |= CAP_UNALIGNED_ACCESS;

	if (epf_test->epc_features->msi_capable)
		caps |= CAP_MSI;

	if (epf_test->epc_features->msix_capable)
		caps |= CAP_MSIX;

	if (epf_test->epc_features->intx_capable)
		caps |= CAP_INTX;

	if (epf_test->epc_features->dynamic_inbound_mapping &&
	    epf_test->epc_features->subrange_mapping)
		caps |= CAP_SUBRANGE_MAPPING;

	reg->caps = cpu_to_le32(caps);
}

static int pci_epf_test_epc_init(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	struct pci_epf_header *header = epf->header;
	const struct pci_epc_features *epc_features = epf_test->epc_features;
	struct pci_epc *epc = epf->epc;
	struct device *dev = &epf->dev;
	bool linkup_notifier = false;
	int ret;

	epf_test->dma_supported = true;

	ret = pci_epf_test_init_dma_chan(epf_test);
	if (ret)
		epf_test->dma_supported = false;

	if (epf->vfunc_no <= 1) {
		ret = pci_epc_write_header(epc, epf->func_no, epf->vfunc_no, header);
		if (ret) {
			dev_err(dev, "Configuration header write failed\n");
			return ret;
		}
	}

	pci_epf_test_set_capabilities(epf);

	ret = pci_epf_test_set_bar(epf);
	if (ret)
		return ret;

	if (epc_features->msi_capable) {
		ret = pci_epc_set_msi(epc, epf->func_no, epf->vfunc_no,
				      epf->msi_interrupts);
		if (ret) {
			dev_err(dev, "MSI configuration failed\n");
			return ret;
		}
	}

	if (epc_features->msix_capable) {
		ret = pci_epc_set_msix(epc, epf->func_no, epf->vfunc_no,
				       epf->msix_interrupts,
				       epf_test->test_reg_bar,
				       epf_test->msix_table_offset);
		if (ret) {
			dev_err(dev, "MSI-X configuration failed\n");
			return ret;
		}
	}

	linkup_notifier = epc_features->linkup_notifier;
	if (!linkup_notifier)
		queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);

	return 0;
}

static void pci_epf_test_epc_deinit(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);

	cancel_delayed_work_sync(&epf_test->cmd_handler);
	pci_epf_test_clean_dma_chan(epf_test);
	pci_epf_test_clear_bar(epf);
}

static int pci_epf_test_link_up(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);

	queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler,
			   msecs_to_jiffies(1));

	return 0;
}

static int pci_epf_test_link_down(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);

	cancel_delayed_work_sync(&epf_test->cmd_handler);

	return 0;
}

static const struct pci_epc_event_ops pci_epf_test_event_ops = {
	.epc_init = pci_epf_test_epc_init,
	.epc_deinit = pci_epf_test_epc_deinit,
	.link_up = pci_epf_test_link_up,
	.link_down = pci_epf_test_link_down,
};

static int pci_epf_test_alloc_space(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	struct device *dev = &epf->dev;
	size_t msix_table_size = 0;
	size_t test_reg_bar_size;
	size_t pba_size = 0;
	void *base;
	enum pci_barno test_reg_bar = epf_test->test_reg_bar;
	enum pci_barno bar;
	const struct pci_epc_features *epc_features = epf_test->epc_features;
	size_t test_reg_size;

	test_reg_bar_size = ALIGN(sizeof(struct pci_epf_test_reg), 128);

	if (epc_features->msix_capable) {
		msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
		epf_test->msix_table_offset = test_reg_bar_size;
		/* Align to QWORD or 8 Bytes */
		pba_size = ALIGN(DIV_ROUND_UP(epf->msix_interrupts, 8), 8);
	}
	test_reg_size = test_reg_bar_size + msix_table_size + pba_size;

	base = pci_epf_alloc_space(epf, test_reg_size, test_reg_bar,
				   epc_features, PRIMARY_INTERFACE);
	if (!base) {
		dev_err(dev, "Failed to allocated register space\n");
		return -ENOMEM;
	}
	epf_test->reg[test_reg_bar] = base;

	for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++) {
		bar = pci_epc_get_next_free_bar(epc_features, bar);
		if (bar == NO_BAR)
			break;

		if (bar == test_reg_bar)
			continue;

		if (epc_features->bar[bar].type == BAR_FIXED)
			test_reg_size = epc_features->bar[bar].fixed_size;
		else
			test_reg_size = epf_test->bar_size[bar];

		base = pci_epf_alloc_space(epf, test_reg_size, bar,
					   epc_features, PRIMARY_INTERFACE);
		if (!base)
			dev_err(dev, "Failed to allocate space for BAR%d\n",
				bar);
		epf_test->reg[bar] = base;
	}

	return 0;
}

static void pci_epf_test_free_space(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	int bar;

	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
		if (!epf_test->reg[bar])
			continue;

		pci_epf_free_space(epf, epf_test->reg[bar], bar,
				   PRIMARY_INTERFACE);
		epf_test->reg[bar] = NULL;
	}
}

static int pci_epf_test_bind(struct pci_epf *epf)
{
	int ret;
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	const struct pci_epc_features *epc_features;
	enum pci_barno test_reg_bar = BAR_0;
	struct pci_epc *epc = epf->epc;

	if (WARN_ON_ONCE(!epc))
		return -EINVAL;

	epc_features = pci_epc_get_features(epc, epf->func_no, epf->vfunc_no);
	if (!epc_features) {
		dev_err(&epf->dev, "epc_features not implemented\n");
		return -EOPNOTSUPP;
	}

	test_reg_bar = pci_epc_get_first_free_bar(epc_features);
	if (test_reg_bar < 0)
		return -EINVAL;

	epf_test->test_reg_bar = test_reg_bar;
	epf_test->epc_features = epc_features;

	ret = pci_epf_test_alloc_space(epf);
	if (ret)
		return ret;

	return 0;
}

static void pci_epf_test_unbind(struct pci_epf *epf)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	struct pci_epc *epc = epf->epc;

	cancel_delayed_work_sync(&epf_test->cmd_handler);
	if (epc->init_complete) {
		pci_epf_test_clean_dma_chan(epf_test);
		pci_epf_test_clear_bar(epf);
	}
	pci_epf_test_free_space(epf);
}

#define PCI_EPF_TEST_BAR_SIZE_R(_name, _id)				\
static ssize_t pci_epf_test_##_name##_show(struct config_item *item,	\
					   char *page)			\
{									\
	struct config_group *group = to_config_group(item);		\
	struct pci_epf_test *epf_test =					\
		container_of(group, struct pci_epf_test, group);	\
									\
	return sysfs_emit(page, "%zu\n", epf_test->bar_size[_id]);	\
}

#define PCI_EPF_TEST_BAR_SIZE_W(_name, _id)				\
static ssize_t pci_epf_test_##_name##_store(struct config_item *item,	\
					    const char *page,		\
					    size_t len)			\
{									\
	struct config_group *group = to_config_group(item);		\
	struct pci_epf_test *epf_test =					\
		container_of(group, struct pci_epf_test, group);	\
	int val, ret;							\
									\
	/*								\
	 * BAR sizes can only be modified before binding to an EPC,	\
	 * because pci_epf_test_alloc_space() is called in .bind().	\
	 */								\
	if (epf_test->epf->epc)						\
		return -EOPNOTSUPP;					\
									\
	ret = kstrtouint(page, 0, &val);				\
	if (ret)							\
		return ret;						\
									\
	if (!is_power_of_2(val))					\
		return -EINVAL;						\
									\
	epf_test->bar_size[_id] = val;					\
									\
	return len;							\
}

PCI_EPF_TEST_BAR_SIZE_R(bar0_size, BAR_0)
PCI_EPF_TEST_BAR_SIZE_W(bar0_size, BAR_0)
PCI_EPF_TEST_BAR_SIZE_R(bar1_size, BAR_1)
PCI_EPF_TEST_BAR_SIZE_W(bar1_size, BAR_1)
PCI_EPF_TEST_BAR_SIZE_R(bar2_size, BAR_2)
PCI_EPF_TEST_BAR_SIZE_W(bar2_size, BAR_2)
PCI_EPF_TEST_BAR_SIZE_R(bar3_size, BAR_3)
PCI_EPF_TEST_BAR_SIZE_W(bar3_size, BAR_3)
PCI_EPF_TEST_BAR_SIZE_R(bar4_size, BAR_4)
PCI_EPF_TEST_BAR_SIZE_W(bar4_size, BAR_4)
PCI_EPF_TEST_BAR_SIZE_R(bar5_size, BAR_5)
PCI_EPF_TEST_BAR_SIZE_W(bar5_size, BAR_5)

CONFIGFS_ATTR(pci_epf_test_, bar0_size);
CONFIGFS_ATTR(pci_epf_test_, bar1_size);
CONFIGFS_ATTR(pci_epf_test_, bar2_size);
CONFIGFS_ATTR(pci_epf_test_, bar3_size);
CONFIGFS_ATTR(pci_epf_test_, bar4_size);
CONFIGFS_ATTR(pci_epf_test_, bar5_size);

static struct configfs_attribute *pci_epf_test_attrs[] = {
	&pci_epf_test_attr_bar0_size,
	&pci_epf_test_attr_bar1_size,
	&pci_epf_test_attr_bar2_size,
	&pci_epf_test_attr_bar3_size,
	&pci_epf_test_attr_bar4_size,
	&pci_epf_test_attr_bar5_size,
	NULL,
};

static const struct config_item_type pci_epf_test_group_type = {
	.ct_attrs	= pci_epf_test_attrs,
	.ct_owner	= THIS_MODULE,
};

static struct config_group *pci_epf_test_add_cfs(struct pci_epf *epf,
						 struct config_group *group)
{
	struct pci_epf_test *epf_test = epf_get_drvdata(epf);
	struct config_group *epf_group = &epf_test->group;
	struct device *dev = &epf->dev;

	config_group_init_type_name(epf_group, dev_name(dev),
				    &pci_epf_test_group_type);

	return epf_group;
}

static const struct pci_epf_device_id pci_epf_test_ids[] = {
	{
		.name = "pci_epf_test",
	},
	{},
};

static int pci_epf_test_probe(struct pci_epf *epf,
			      const struct pci_epf_device_id *id)
{
	struct pci_epf_test *epf_test;
	struct device *dev = &epf->dev;
	enum pci_barno bar;

	epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
	if (!epf_test)
		return -ENOMEM;

	epf->header = &test_header;
	epf_test->epf = epf;
	for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++)
		epf_test->bar_size[bar] = default_bar_size[bar];

	INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);

	epf->event_ops = &pci_epf_test_event_ops;

	epf_set_drvdata(epf, epf_test);
	return 0;
}

static const struct pci_epf_ops ops = {
	.unbind	= pci_epf_test_unbind,
	.bind	= pci_epf_test_bind,
	.add_cfs = pci_epf_test_add_cfs,
};

static struct pci_epf_driver test_driver = {
	.driver.name	= "pci_epf_test",
	.probe		= pci_epf_test_probe,
	.id_table	= pci_epf_test_ids,
	.ops		= &ops,
	.owner		= THIS_MODULE,
};

static int __init pci_epf_test_init(void)
{
	int ret;

	kpcitest_workqueue = alloc_workqueue("kpcitest",
				    WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0);
	if (!kpcitest_workqueue) {
		pr_err("Failed to allocate the kpcitest work queue\n");
		return -ENOMEM;
	}

	ret = pci_epf_register_driver(&test_driver);
	if (ret) {
		destroy_workqueue(kpcitest_workqueue);
		pr_err("Failed to register pci epf test driver --> %d\n", ret);
		return ret;
	}

	return 0;
}
module_init(pci_epf_test_init);

static void __exit pci_epf_test_exit(void)
{
	if (kpcitest_workqueue)
		destroy_workqueue(kpcitest_workqueue);
	pci_epf_unregister_driver(&test_driver);
}
module_exit(pci_epf_test_exit);

MODULE_DESCRIPTION("PCI EPF TEST DRIVER");
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
MODULE_LICENSE("GPL v2");