File Index
Files and directories
Sign in
FILE

pci-ep-msi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pci/endpoint/pci-ep-msi.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * PCI Endpoint *Controller* (EPC) MSI library
  4 *
  5 * Copyright (C) 2025 NXP
  6 * Author: Frank Li <Frank.Li@nxp.com>
  7 */
  8
  9#include <linux/device.h>
 10#include <linux/export.h>
 11#include <linux/irqdomain.h>
 12#include <linux/module.h>
 13#include <linux/msi.h>
 14#include <linux/of_irq.h>
 15#include <linux/pci-epc.h>
 16#include <linux/pci-epf.h>
 17#include <linux/pci-ep-cfs.h>
 18#include <linux/pci-ep-msi.h>
 19#include <linux/slab.h>
 20
 21static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg)
 22{
 23	struct pci_epc *epc;
 24	struct pci_epf *epf;
 25
 26	epc = pci_epc_get(dev_name(msi_desc_to_dev(desc)));
 27	if (IS_ERR(epc))
 28		return;
 29
 30	epf = list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list);
 31
 32	if (epf && epf->db_msg && desc->msi_index < epf->num_db)
 33		memcpy(&epf->db_msg[desc->msi_index].msg, msg, sizeof(*msg));
 34
 35	pci_epc_put(epc);
 36}
 37
 38int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
 39{
 40	struct pci_epc *epc = epf->epc;
 41	struct device *dev = &epf->dev;
 42	struct irq_domain *domain;
 43	void *msg;
 44	int ret;
 45	int i;
 46
 47	/* TODO: Multi-EPF support */
 48	if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) {
 49		dev_err(dev, "MSI doorbell doesn't support multiple EPF\n");
 50		return -EINVAL;
 51	}
 52
 53	domain = of_msi_map_get_device_domain(epc->dev.parent, 0,
 54					      DOMAIN_BUS_PLATFORM_MSI);
 55	if (!domain) {
 56		dev_err(dev, "Can't find MSI domain for EPC\n");
 57		return -ENODEV;
 58	}
 59
 60	if (!irq_domain_is_msi_parent(domain))
 61		return -ENODEV;
 62
 63	if (!irq_domain_is_msi_immutable(domain)) {
 64		dev_err(dev, "Mutable MSI controller not supported\n");
 65		return -ENODEV;
 66	}
 67
 68	dev_set_msi_domain(epc->dev.parent, domain);
 69
 70	msg = kcalloc(num_db, sizeof(struct pci_epf_doorbell_msg), GFP_KERNEL);
 71	if (!msg)
 72		return -ENOMEM;
 73
 74	epf->num_db = num_db;
 75	epf->db_msg = msg;
 76
 77	ret = platform_device_msi_init_and_alloc_irqs(epc->dev.parent, num_db,
 78						      pci_epf_write_msi_msg);
 79	if (ret) {
 80		dev_err(dev, "Failed to allocate MSI\n");
 81		kfree(msg);
 82		return ret;
 83	}
 84
 85	for (i = 0; i < num_db; i++)
 86		epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i);
 87
 88	return ret;
 89}
 90EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell);
 91
 92void pci_epf_free_doorbell(struct pci_epf *epf)
 93{
 94	platform_device_msi_free_irqs_all(epf->epc->dev.parent);
 95
 96	kfree(epf->db_msg);
 97	epf->db_msg = NULL;
 98	epf->num_db = 0;
 99}
100EXPORT_SYMBOL_GPL(pci_epf_free_doorbell);
~/ Read-only file preview.