@@ -419,3 +419,134 @@ int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
return err;
}
+
+#ifdef CONFIG_DEVICE_MSI
+/*
+ * Device specific MSI domain infrastructure for devices which have their
+ * own resource management and interrupt chip. These devices are not
+ * related to PCI and contrary to platform MSI they do not share a common
+ * resource and interrupt chip. They provide their own domain specific
+ * resource management and interrupt chip.
+ */
+
+static void device_msi_free_msi_entries(struct device *dev)
+{
+ struct list_head *msi_list = dev_to_msi_list(dev);
+ struct msi_desc *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, msi_list, list) {
+ list_del(&entry->list);
+ free_msi_entry(entry);
+ }
+}
+
+/**
+ * device_msi_free_irqs - Free MSI interrupts assigned to a device
+ * @dev: Pointer to the device
+ *
+ * Frees the interrupt and the MSI descriptors.
+ */
+static void device_msi_free_irqs(struct irq_domain *domain, struct device *dev)
+{
+ __msi_domain_free_irqs(domain, dev);
+ device_msi_free_msi_entries(dev);
+}
+
+/**
+ * device_msi_alloc_irqs - Allocate MSI interrupts for a device
+ * @dev: Pointer to the device
+ * @nvec: Number of vectors
+ *
+ * Allocates the required number of MSI descriptors and the corresponding
+ * interrupt descriptors.
+ */
+static int device_msi_alloc_irqs(struct irq_domain *domain, struct device *dev, int nvec)
+{
+ int i, ret = -ENOMEM;
+
+ for (i = 0; i < nvec; i++) {
+ struct msi_desc *entry = alloc_msi_entry(dev, 1, NULL);
+
+ if (!entry)
+ goto fail;
+ list_add_tail(&entry->list, dev_to_msi_list(dev));
+ }
+
+ ret = __msi_domain_alloc_irqs(domain, dev, nvec);
+ if (!ret)
+ return 0;
+fail:
+ device_msi_free_msi_entries(dev);
+ return ret;
+}
+
+static void device_msi_update_dom_ops(struct msi_domain_info *info)
+{
+ if (!info->ops->domain_alloc_irqs)
+ info->ops->domain_alloc_irqs = device_msi_alloc_irqs;
+ if (!info->ops->domain_free_irqs)
+ info->ops->domain_free_irqs = device_msi_free_irqs;
+ if (!info->ops->msi_prepare)
+ info->ops->msi_prepare = arch_msi_prepare;
+}
+
+/**
+ * device_msi_create_msi_irq_domain - Create an irq domain for devices
+ * @fwnode: Firmware node of the interrupt controller
+ * @info: MSI domain info to configure the new domain
+ * @parent: Parent domain
+ */
+struct irq_domain *device_msi_create_irq_domain(struct fwnode_handle *fn,
+ struct msi_domain_info *info,
+ struct irq_domain *parent)
+{
+ struct irq_domain *domain;
+
+ if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
+ platform_msi_update_chip_ops(info);
+
+ if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
+ device_msi_update_dom_ops(info);
+
+ msi_domain_set_default_info_flags(info);
+
+ domain = msi_create_irq_domain(fn, info, parent);
+ if (domain)
+ irq_domain_update_bus_token(domain, DOMAIN_BUS_DEVICE_MSI);
+ return domain;
+}
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+
+/**
+ * pci_subdevice_msi_create_irq_domain - Create an irq domain for subdevices
+ * @pdev: Pointer to PCI device for which the subdevice domain is created
+ * @info: MSI domain info to configure the new domain
+ */
+struct irq_domain *pci_subdevice_msi_create_irq_domain(struct pci_dev *pdev,
+ struct msi_domain_info *info)
+{
+ struct irq_domain *domain, *pdev_msi;
+ struct fwnode_handle *fn;
+
+ /*
+ * Retrieve the MSI domain of the underlying PCI device's MSI
+ * domain. The PCI device domain's parent domain is also the parent
+ * domain of the new subdevice domain.
+ */
+ pdev_msi = dev_get_msi_domain(&pdev->dev);
+ if (!pdev_msi)
+ return NULL;
+
+ fn = irq_domain_alloc_named_fwnode(dev_name(&pdev->dev));
+ if (!fn)
+ return NULL;
+ domain = device_msi_create_irq_domain(fn, info, pdev_msi->parent);
+ if (!domain)
+ irq_domain_free_fwnode(fn);
+ return domain;
+}
+EXPORT_SYMBOL_GPL(pci_subdevice_msi_create_irq_domain);
+#endif /* CONFIG_PCI */
+#endif /* CONFIG_DEVICE_MSI */
@@ -85,6 +85,7 @@ enum irq_domain_bus_token {
DOMAIN_BUS_TI_SCI_INTA_MSI,
DOMAIN_BUS_WAKEUP,
DOMAIN_BUS_VMD_MSI,
+ DOMAIN_BUS_DEVICE_MSI,
};
/**
@@ -95,6 +95,18 @@ struct ti_sci_inta_msi_desc {
};
/**
+ * device_msi_desc - Device MSI specific MSI descriptor data
+ * @priv: Pointer to device specific private data
+ * @priv_iomem: Pointer to device specific private io memory
+ * @hwirq: The hardware irq number in the device domain
+ */
+struct device_msi_desc {
+ void *priv;
+ void __iomem *priv_iomem;
+ u16 hwirq;
+};
+
+/**
* struct msi_desc - Descriptor structure for MSI based interrupts
* @list: List head for management
* @irq: The base interrupt number
@@ -166,6 +178,7 @@ struct msi_desc {
struct platform_msi_desc platform;
struct fsl_mc_msi_desc fsl_mc;
struct ti_sci_inta_msi_desc inta;
+ struct device_msi_desc device_msi;
};
};
@@ -451,6 +464,17 @@ void *platform_msi_get_host_data(struct irq_domain *domain);
void msi_domain_set_default_info_flags(struct msi_domain_info *info);
#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
+#ifdef CONFIG_DEVICE_MSI
+struct irq_domain *device_msi_create_irq_domain(struct fwnode_handle *fn,
+ struct msi_domain_info *info,
+ struct irq_domain *parent);
+
+# ifdef CONFIG_PCI
+struct irq_domain *pci_subdevice_msi_create_irq_domain(struct pci_dev *pdev,
+ struct msi_domain_info *info);
+# endif
+#endif /* CONFIG_DEVICE_MSI */
+
#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg);
struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
@@ -89,6 +89,10 @@ config GENERIC_MSI_IRQ_DOMAIN
select IRQ_DOMAIN_HIERARCHY
select GENERIC_MSI_IRQ
+config DEVICE_MSI
+ bool
+ select GENERIC_MSI_IRQ_DOMAIN
+
config IRQ_MSI_IOMMU
bool