Commit 63e6f0df authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/endpoint/doorbell'

- Add RC-to-EP doorbell support using platform MSI controller (Frank Li)

- Check for MSI parent and mutability since we currently don't support
  mutable MSI controllers (Frank Li)

- Add pci_epf_align_inbound_addr() helper (Frank Li)

- Add a doorbell test (Frank Li)

* pci/endpoint/doorbell:
  selftests: pci_endpoint: Add doorbell test case
  misc: pci_endpoint_test: Add doorbell test case
  PCI: endpoint: pci-epf-test: Add doorbell test support
  PCI: endpoint: Add pci_epf_align_inbound_addr() helper for inbound address alignment
  PCI: endpoint: pci-ep-msi: Add checks for MSI parent and mutability
  PCI: endpoint: Add RC-to-EP doorbell support using platform MSI controller
parents b8222fe2 b351e9c9
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -203,3 +203,18 @@ controllers, it is advisable to skip this testcase using this
command::

	# pci_endpoint_test -f pci_ep_bar -f pci_ep_basic -v memcpy -T COPY_TEST -v dma

Kselftest EP Doorbell
~~~~~~~~~~~~~~~~~~~~~

If the Endpoint MSI controller is used for the doorbell usecase, run below
command for testing it:

	# pci_endpoint_test -f pcie_ep_doorbell

	# Starting 1 tests from 1 test cases.
	#  RUN           pcie_ep_doorbell.DOORBELL_TEST ...
	#            OK  pcie_ep_doorbell.DOORBELL_TEST
	ok 1 pcie_ep_doorbell.DOORBELL_TEST
	# PASSED: 1 / 1 tests passed.
	# Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
+83 −0
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@
#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 PCI_ENDPOINT_TEST_STATUS		0x8
#define STATUS_READ_SUCCESS			BIT(0)
@@ -48,6 +50,11 @@
#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 PCI_ENDPOINT_TEST_LOWER_SRC_ADDR	0x0c
#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR	0x10
@@ -62,6 +69,7 @@
#define PCI_ENDPOINT_TEST_IRQ_NUMBER		0x28

#define PCI_ENDPOINT_TEST_FLAGS			0x2c

#define FLAG_USE_DMA				BIT(0)

#define PCI_ENDPOINT_TEST_CAPS			0x30
@@ -70,6 +78,10 @@
#define CAP_MSIX				BIT(2)
#define CAP_INTX				BIT(3)

#define PCI_ENDPOINT_TEST_DB_BAR		0x34
#define PCI_ENDPOINT_TEST_DB_OFFSET		0x38
#define PCI_ENDPOINT_TEST_DB_DATA		0x3c

#define PCI_DEVICE_ID_TI_AM654			0xb00c
#define PCI_DEVICE_ID_TI_J7200			0xb00f
#define PCI_DEVICE_ID_TI_AM64			0xb010
@@ -100,6 +112,7 @@ enum pci_barno {
	BAR_3,
	BAR_4,
	BAR_5,
	NO_BAR = -1,
};

struct pci_endpoint_test {
@@ -841,6 +854,73 @@ static int pci_endpoint_test_set_irq(struct pci_endpoint_test *test,
	return 0;
}

static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
{
	struct pci_dev *pdev = test->pdev;
	struct device *dev = &pdev->dev;
	int irq_type = test->irq_type;
	enum pci_barno bar;
	u32 data, status;
	u32 addr;
	int left;

	if (irq_type < PCITEST_IRQ_TYPE_INTX ||
	    irq_type > PCITEST_IRQ_TYPE_MSIX) {
		dev_err(dev, "Invalid IRQ type\n");
		return -EINVAL;
	}

	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
				 COMMAND_ENABLE_DOORBELL);

	left = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));

	status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
	if (!left || (status & STATUS_DOORBELL_ENABLE_FAIL)) {
		dev_err(dev, "Failed to enable doorbell\n");
		return -EINVAL;
	}

	data = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_DATA);
	addr = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_OFFSET);
	bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);

	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);

	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);

	bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);

	writel(data, test->bar[bar] + addr);

	left = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));

	status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);

	if (!left || !(status & STATUS_DOORBELL_SUCCESS))
		dev_err(dev, "Failed to trigger doorbell in endpoint\n");

	pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
				 COMMAND_DISABLE_DOORBELL);

	wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));

	status |= pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);

	if (status & STATUS_DOORBELL_DISABLE_FAIL) {
		dev_err(dev, "Failed to disable doorbell\n");
		return -EINVAL;
	}

	if (!(status & STATUS_DOORBELL_SUCCESS))
		return -EINVAL;

	return 0;
}

static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
				    unsigned long arg)
{
@@ -891,6 +971,9 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
	case PCITEST_CLEAR_IRQ:
		ret = pci_endpoint_test_clear_irq(test);
		break;
	case PCITEST_DOORBELL:
		ret = pci_endpoint_test_doorbell(test);
		break;
	}

ret:
+8 −0
Original line number Diff line number Diff line
@@ -28,6 +28,14 @@ config PCI_ENDPOINT_CONFIGFS
	   configure the endpoint function and used to bind the
	   function with an endpoint controller.

config PCI_ENDPOINT_MSI_DOORBELL
	bool "PCI Endpoint MSI Doorbell Support"
	depends on PCI_ENDPOINT && GENERIC_MSI_IRQ
	help
	  This enables the EP's MSI interrupt controller to function as a
	  doorbell. The RC can trigger doorbell in EP by writing data to a
	  dedicated BAR, which the EP maps to the controller's message address.

source "drivers/pci/endpoint/functions/Kconfig"

endmenu
+1 −0
Original line number Diff line number Diff line
@@ -6,3 +6,4 @@
obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS)	+= pci-ep-cfs.o
obj-$(CONFIG_PCI_ENDPOINT)		+= pci-epc-core.o pci-epf-core.o\
					   pci-epc-mem.o functions/
obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL)	+= pci-ep-msi.o
+130 −0
Original line number Diff line number Diff line
@@ -11,12 +11,14 @@
#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
@@ -29,6 +31,8 @@
#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 STATUS_READ_SUCCESS		BIT(0)
#define STATUS_READ_FAIL		BIT(1)
@@ -39,6 +43,11 @@
#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 FLAG_USE_DMA			BIT(0)

@@ -66,6 +75,7 @@ struct pci_epf_test {
	bool			dma_supported;
	bool			dma_private;
	const struct pci_epc_features *epc_features;
	struct pci_epf_bar	db_bar;
};

struct pci_epf_test_reg {
@@ -80,6 +90,9 @@ struct pci_epf_test_reg {
	__le32 irq_number;
	__le32 flags;
	__le32 caps;
	__le32 doorbell_bar;
	__le32 doorbell_offset;
	__le32 doorbell_data;
} __packed;

static struct pci_epf_header test_header = {
@@ -667,6 +680,115 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,
	}
}

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_irq(epf->db_msg[0].virq, pci_epf_test_doorbell_handler, 0,
			  "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;

	if (bar < BAR_0)
		goto set_status_err;

	pci_epf_test_doorbell_cleanup(epf_test);
	pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);

	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 void pci_epf_test_cmd_handler(struct work_struct *work)
{
	u32 command;
@@ -714,6 +836,14 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
		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;
	default:
		dev_err(dev, "Invalid command 0x%x\n", command);
		break;
Loading