]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
PCI: endpoint: pci-epf-test: Reuse pre-exposed doorbell targets
authorKoichiro Den <den@valinux.co.jp>
Tue, 14 Apr 2026 14:15:13 +0000 (23:15 +0900)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 4 May 2026 23:01:33 +0000 (18:01 -0500)
pci-epf-test advertises the doorbell target to the RC as a BAR number
and an offset, and the RC rings the doorbell with a single DWORD MMIO
write.

Some doorbell backends may report that the doorbell target is already
exposed via a platform-owned fixed BAR (db_msg[0].bar/offset). In that
case, reuse the pre-exposed window and do not reprogram the BAR with
pci_epc_set_bar().

Also honor db_msg[0].irq_flags when requesting the doorbell IRQ, and
only restore the original BAR mapping on disable if pci-epf-test
programmed it.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
[bhelgaas: wrap comment]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260414141514.1341429-7-den@valinux.co.jp
drivers/pci/endpoint/functions/pci-epf-test.c

index 591d301fa89d89addf5df16e775e80460b689589..4802d4f80f780fd0e89dbdd0f67f12013da31601 100644 (file)
@@ -94,6 +94,7 @@ struct pci_epf_test {
        bool                    dma_private;
        const struct pci_epc_features *epc_features;
        struct pci_epf_bar      db_bar;
+       bool                    db_bar_programmed;
        size_t                  bar_size[PCI_STD_NUM_BARS];
 };
 
@@ -733,7 +734,9 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
 {
        u32 status = le32_to_cpu(reg->status);
        struct pci_epf *epf = epf_test->epf;
+       struct pci_epf_doorbell_msg *db;
        struct pci_epc *epc = epf->epc;
+       unsigned long irq_flags;
        struct msi_msg *msg;
        enum pci_barno bar;
        size_t offset;
@@ -743,13 +746,28 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
        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;
+       db = &epf->db_msg[0];
+       msg = &db->msg;
+       epf_test->db_bar_programmed = false;
+
+       if (db->bar != NO_BAR) {
+               /*
+                * The doorbell target is already exposed via a platform-owned
+                * fixed BAR
+                */
+               bar = db->bar;
+               offset = db->offset;
+       } else {
+               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;
+       }
+
+       irq_flags = epf->db_msg[0].irq_flags | IRQF_ONESHOT;
 
        ret = request_threaded_irq(epf->db_msg[0].virq, NULL,
-                                  pci_epf_test_doorbell_handler, IRQF_ONESHOT,
+                                  pci_epf_test_doorbell_handler, irq_flags,
                                   "pci-ep-test-doorbell", epf_test);
        if (ret) {
                dev_err(&epf->dev,
@@ -761,22 +779,30 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
        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 (db->bar == NO_BAR) {
+               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_free_irq;
+               if (ret)
+                       goto err_free_irq;
+       }
 
        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;
+       if (db->bar == NO_BAR) {
+               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_free_irq;
+               ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
+               if (ret)
+                       goto err_free_irq;
+
+               epf_test->db_bar_programmed = true;
+       }
 
        status |= STATUS_DOORBELL_ENABLE_SUCCESS;
        reg->status = cpu_to_le32(status);
@@ -806,17 +832,23 @@ static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
        free_irq(epf->db_msg[0].virq, epf_test);
        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;
+       if (epf_test->db_bar_programmed) {
+               /*
+                * 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;
+
+               epf_test->db_bar_programmed = false;
+       }
 
        status |= STATUS_DOORBELL_DISABLE_SUCCESS;
        reg->status = cpu_to_le32(status);