mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 20:57:31 -04:00
PCI: endpoint: pci-epf-test: Reuse pre-exposed doorbell targets
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
This commit is contained in:
committed by
Bjorn Helgaas
parent
0c3f82a584
commit
8fda2dd209
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user