Skip to content

Commit 63e6f0d

Browse files
committed
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
2 parents b8222fe + b351e9c commit 63e6f0d

File tree

11 files changed

+450
-0
lines changed

11 files changed

+450
-0
lines changed

Documentation/PCI/endpoint/pci-test-howto.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,18 @@ controllers, it is advisable to skip this testcase using this
203203
command::
204204

205205
# pci_endpoint_test -f pci_ep_bar -f pci_ep_basic -v memcpy -T COPY_TEST -v dma
206+
207+
Kselftest EP Doorbell
208+
~~~~~~~~~~~~~~~~~~~~~
209+
210+
If the Endpoint MSI controller is used for the doorbell usecase, run below
211+
command for testing it:
212+
213+
# pci_endpoint_test -f pcie_ep_doorbell
214+
215+
# Starting 1 tests from 1 test cases.
216+
# RUN pcie_ep_doorbell.DOORBELL_TEST ...
217+
# OK pcie_ep_doorbell.DOORBELL_TEST
218+
ok 1 pcie_ep_doorbell.DOORBELL_TEST
219+
# PASSED: 1 / 1 tests passed.
220+
# Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0

drivers/misc/pci_endpoint_test.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
#define COMMAND_READ BIT(3)
3838
#define COMMAND_WRITE BIT(4)
3939
#define COMMAND_COPY BIT(5)
40+
#define COMMAND_ENABLE_DOORBELL BIT(6)
41+
#define COMMAND_DISABLE_DOORBELL BIT(7)
4042

4143
#define PCI_ENDPOINT_TEST_STATUS 0x8
4244
#define STATUS_READ_SUCCESS BIT(0)
@@ -48,6 +50,11 @@
4850
#define STATUS_IRQ_RAISED BIT(6)
4951
#define STATUS_SRC_ADDR_INVALID BIT(7)
5052
#define STATUS_DST_ADDR_INVALID BIT(8)
53+
#define STATUS_DOORBELL_SUCCESS BIT(9)
54+
#define STATUS_DOORBELL_ENABLE_SUCCESS BIT(10)
55+
#define STATUS_DOORBELL_ENABLE_FAIL BIT(11)
56+
#define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12)
57+
#define STATUS_DOORBELL_DISABLE_FAIL BIT(13)
5158

5259
#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c
5360
#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10
@@ -62,6 +69,7 @@
6269
#define PCI_ENDPOINT_TEST_IRQ_NUMBER 0x28
6370

6471
#define PCI_ENDPOINT_TEST_FLAGS 0x2c
72+
6573
#define FLAG_USE_DMA BIT(0)
6674

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

81+
#define PCI_ENDPOINT_TEST_DB_BAR 0x34
82+
#define PCI_ENDPOINT_TEST_DB_OFFSET 0x38
83+
#define PCI_ENDPOINT_TEST_DB_DATA 0x3c
84+
7385
#define PCI_DEVICE_ID_TI_AM654 0xb00c
7486
#define PCI_DEVICE_ID_TI_J7200 0xb00f
7587
#define PCI_DEVICE_ID_TI_AM64 0xb010
@@ -100,6 +112,7 @@ enum pci_barno {
100112
BAR_3,
101113
BAR_4,
102114
BAR_5,
115+
NO_BAR = -1,
103116
};
104117

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

857+
static int pci_endpoint_test_doorbell(struct pci_endpoint_test *test)
858+
{
859+
struct pci_dev *pdev = test->pdev;
860+
struct device *dev = &pdev->dev;
861+
int irq_type = test->irq_type;
862+
enum pci_barno bar;
863+
u32 data, status;
864+
u32 addr;
865+
int left;
866+
867+
if (irq_type < PCITEST_IRQ_TYPE_INTX ||
868+
irq_type > PCITEST_IRQ_TYPE_MSIX) {
869+
dev_err(dev, "Invalid IRQ type\n");
870+
return -EINVAL;
871+
}
872+
873+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
874+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
875+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
876+
COMMAND_ENABLE_DOORBELL);
877+
878+
left = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));
879+
880+
status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
881+
if (!left || (status & STATUS_DOORBELL_ENABLE_FAIL)) {
882+
dev_err(dev, "Failed to enable doorbell\n");
883+
return -EINVAL;
884+
}
885+
886+
data = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_DATA);
887+
addr = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_OFFSET);
888+
bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);
889+
890+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
891+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
892+
893+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
894+
895+
bar = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_DB_BAR);
896+
897+
writel(data, test->bar[bar] + addr);
898+
899+
left = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));
900+
901+
status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
902+
903+
if (!left || !(status & STATUS_DOORBELL_SUCCESS))
904+
dev_err(dev, "Failed to trigger doorbell in endpoint\n");
905+
906+
pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND,
907+
COMMAND_DISABLE_DOORBELL);
908+
909+
wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000));
910+
911+
status |= pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
912+
913+
if (status & STATUS_DOORBELL_DISABLE_FAIL) {
914+
dev_err(dev, "Failed to disable doorbell\n");
915+
return -EINVAL;
916+
}
917+
918+
if (!(status & STATUS_DOORBELL_SUCCESS))
919+
return -EINVAL;
920+
921+
return 0;
922+
}
923+
844924
static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
845925
unsigned long arg)
846926
{
@@ -891,6 +971,9 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
891971
case PCITEST_CLEAR_IRQ:
892972
ret = pci_endpoint_test_clear_irq(test);
893973
break;
974+
case PCITEST_DOORBELL:
975+
ret = pci_endpoint_test_doorbell(test);
976+
break;
894977
}
895978

896979
ret:

drivers/pci/endpoint/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ config PCI_ENDPOINT_CONFIGFS
2828
configure the endpoint function and used to bind the
2929
function with an endpoint controller.
3030

31+
config PCI_ENDPOINT_MSI_DOORBELL
32+
bool "PCI Endpoint MSI Doorbell Support"
33+
depends on PCI_ENDPOINT && GENERIC_MSI_IRQ
34+
help
35+
This enables the EP's MSI interrupt controller to function as a
36+
doorbell. The RC can trigger doorbell in EP by writing data to a
37+
dedicated BAR, which the EP maps to the controller's message address.
38+
3139
source "drivers/pci/endpoint/functions/Kconfig"
3240

3341
endmenu

drivers/pci/endpoint/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o
77
obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\
88
pci-epc-mem.o functions/
9+
obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL) += pci-ep-msi.o

drivers/pci/endpoint/functions/pci-epf-test.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
#include <linux/dmaengine.h>
1212
#include <linux/io.h>
1313
#include <linux/module.h>
14+
#include <linux/msi.h>
1415
#include <linux/slab.h>
1516
#include <linux/pci_ids.h>
1617
#include <linux/random.h>
1718

1819
#include <linux/pci-epc.h>
1920
#include <linux/pci-epf.h>
21+
#include <linux/pci-ep-msi.h>
2022
#include <linux/pci_regs.h>
2123

2224
#define IRQ_TYPE_INTX 0
@@ -29,6 +31,8 @@
2931
#define COMMAND_READ BIT(3)
3032
#define COMMAND_WRITE BIT(4)
3133
#define COMMAND_COPY BIT(5)
34+
#define COMMAND_ENABLE_DOORBELL BIT(6)
35+
#define COMMAND_DISABLE_DOORBELL BIT(7)
3236

3337
#define STATUS_READ_SUCCESS BIT(0)
3438
#define STATUS_READ_FAIL BIT(1)
@@ -39,6 +43,11 @@
3943
#define STATUS_IRQ_RAISED BIT(6)
4044
#define STATUS_SRC_ADDR_INVALID BIT(7)
4145
#define STATUS_DST_ADDR_INVALID BIT(8)
46+
#define STATUS_DOORBELL_SUCCESS BIT(9)
47+
#define STATUS_DOORBELL_ENABLE_SUCCESS BIT(10)
48+
#define STATUS_DOORBELL_ENABLE_FAIL BIT(11)
49+
#define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12)
50+
#define STATUS_DOORBELL_DISABLE_FAIL BIT(13)
4251

4352
#define FLAG_USE_DMA BIT(0)
4453

@@ -66,6 +75,7 @@ struct pci_epf_test {
6675
bool dma_supported;
6776
bool dma_private;
6877
const struct pci_epc_features *epc_features;
78+
struct pci_epf_bar db_bar;
6979
};
7080

7181
struct pci_epf_test_reg {
@@ -80,6 +90,9 @@ struct pci_epf_test_reg {
8090
__le32 irq_number;
8191
__le32 flags;
8292
__le32 caps;
93+
__le32 doorbell_bar;
94+
__le32 doorbell_offset;
95+
__le32 doorbell_data;
8396
} __packed;
8497

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

683+
static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
684+
{
685+
struct pci_epf_test *epf_test = data;
686+
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
687+
struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
688+
u32 status = le32_to_cpu(reg->status);
689+
690+
status |= STATUS_DOORBELL_SUCCESS;
691+
reg->status = cpu_to_le32(status);
692+
pci_epf_test_raise_irq(epf_test, reg);
693+
694+
return IRQ_HANDLED;
695+
}
696+
697+
static void pci_epf_test_doorbell_cleanup(struct pci_epf_test *epf_test)
698+
{
699+
struct pci_epf_test_reg *reg = epf_test->reg[epf_test->test_reg_bar];
700+
struct pci_epf *epf = epf_test->epf;
701+
702+
free_irq(epf->db_msg[0].virq, epf_test);
703+
reg->doorbell_bar = cpu_to_le32(NO_BAR);
704+
705+
pci_epf_free_doorbell(epf);
706+
}
707+
708+
static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
709+
struct pci_epf_test_reg *reg)
710+
{
711+
u32 status = le32_to_cpu(reg->status);
712+
struct pci_epf *epf = epf_test->epf;
713+
struct pci_epc *epc = epf->epc;
714+
struct msi_msg *msg;
715+
enum pci_barno bar;
716+
size_t offset;
717+
int ret;
718+
719+
ret = pci_epf_alloc_doorbell(epf, 1);
720+
if (ret)
721+
goto set_status_err;
722+
723+
msg = &epf->db_msg[0].msg;
724+
bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1);
725+
if (bar < BAR_0)
726+
goto err_doorbell_cleanup;
727+
728+
ret = request_irq(epf->db_msg[0].virq, pci_epf_test_doorbell_handler, 0,
729+
"pci-ep-test-doorbell", epf_test);
730+
if (ret) {
731+
dev_err(&epf->dev,
732+
"Failed to request doorbell IRQ: %d\n",
733+
epf->db_msg[0].virq);
734+
goto err_doorbell_cleanup;
735+
}
736+
737+
reg->doorbell_data = cpu_to_le32(msg->data);
738+
reg->doorbell_bar = cpu_to_le32(bar);
739+
740+
msg = &epf->db_msg[0].msg;
741+
ret = pci_epf_align_inbound_addr(epf, bar, ((u64)msg->address_hi << 32) | msg->address_lo,
742+
&epf_test->db_bar.phys_addr, &offset);
743+
744+
if (ret)
745+
goto err_doorbell_cleanup;
746+
747+
reg->doorbell_offset = cpu_to_le32(offset);
748+
749+
epf_test->db_bar.barno = bar;
750+
epf_test->db_bar.size = epf->bar[bar].size;
751+
epf_test->db_bar.flags = epf->bar[bar].flags;
752+
753+
ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
754+
if (ret)
755+
goto err_doorbell_cleanup;
756+
757+
status |= STATUS_DOORBELL_ENABLE_SUCCESS;
758+
reg->status = cpu_to_le32(status);
759+
return;
760+
761+
err_doorbell_cleanup:
762+
pci_epf_test_doorbell_cleanup(epf_test);
763+
set_status_err:
764+
status |= STATUS_DOORBELL_ENABLE_FAIL;
765+
reg->status = cpu_to_le32(status);
766+
}
767+
768+
static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
769+
struct pci_epf_test_reg *reg)
770+
{
771+
enum pci_barno bar = le32_to_cpu(reg->doorbell_bar);
772+
u32 status = le32_to_cpu(reg->status);
773+
struct pci_epf *epf = epf_test->epf;
774+
struct pci_epc *epc = epf->epc;
775+
776+
if (bar < BAR_0)
777+
goto set_status_err;
778+
779+
pci_epf_test_doorbell_cleanup(epf_test);
780+
pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
781+
782+
status |= STATUS_DOORBELL_DISABLE_SUCCESS;
783+
reg->status = cpu_to_le32(status);
784+
785+
return;
786+
787+
set_status_err:
788+
status |= STATUS_DOORBELL_DISABLE_FAIL;
789+
reg->status = cpu_to_le32(status);
790+
}
791+
670792
static void pci_epf_test_cmd_handler(struct work_struct *work)
671793
{
672794
u32 command;
@@ -714,6 +836,14 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
714836
pci_epf_test_copy(epf_test, reg);
715837
pci_epf_test_raise_irq(epf_test, reg);
716838
break;
839+
case COMMAND_ENABLE_DOORBELL:
840+
pci_epf_test_enable_doorbell(epf_test, reg);
841+
pci_epf_test_raise_irq(epf_test, reg);
842+
break;
843+
case COMMAND_DISABLE_DOORBELL:
844+
pci_epf_test_disable_doorbell(epf_test, reg);
845+
pci_epf_test_raise_irq(epf_test, reg);
846+
break;
717847
default:
718848
dev_err(dev, "Invalid command 0x%x\n", command);
719849
break;

0 commit comments

Comments
 (0)