@@ -18,6 +18,8 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip/arm-gic.h>
+#include <linux/iommu.h>
+#include <linux/msi.h>
#include "irq-gic-common.h"
@@ -121,3 +123,61 @@ void gic_cpu_config(void __iomem *base, void (*sync_access)(void))
if (sync_access)
sync_access();
}
+
+int gic_set_msi_addr(struct irq_data *data, struct msi_msg *msg)
+{
+ struct msi_desc *desc = irq_data_get_msi_desc(data);
+ struct device *dev = msi_desc_to_dev(desc);
+ struct iommu_domain *d;
+ phys_addr_t addr;
+ dma_addr_t iova;
+ int ret;
+
+ d = iommu_get_domain_for_dev(dev);
+ if (!d)
+ return 0;
+
+ addr = ((phys_addr_t)(msg->address_hi) << 32) |
+ msg->address_lo;
+
+ ret = iommu_get_single_reserved(d, addr, IOMMU_WRITE, &iova);
+
+ if (!ret) {
+ msg->address_lo = lower_32_bits(iova);
+ msg->address_hi = upper_32_bits(iova);
+ }
+ return ret;
+}
+
+
+void gic_unset_msi_addr(struct irq_data *data)
+{
+ struct msi_desc *desc = irq_data_get_msi_desc(data);
+ struct device *dev;
+ struct iommu_domain *d;
+ dma_addr_t iova;
+
+ iova = ((dma_addr_t)(desc->msg.address_hi) << 32) |
+ desc->msg.address_lo;
+
+ dev = msi_desc_to_dev(desc);
+ if (!dev)
+ return;
+
+ d = iommu_get_domain_for_dev(dev);
+ if (!d)
+ return;
+
+ iommu_put_single_reserved(d, iova);
+}
+
+void gic_pci_msi_domain_write_msg(struct irq_data *irq_data,
+ struct msi_msg *msg)
+{
+ if (!msg->address_hi && !msg->address_lo && !msg->data)
+ gic_unset_msi_addr(irq_data); /* deactivate */
+ else
+ gic_set_msi_addr(irq_data, msg); /* activate, set_affinity */
+
+ pci_msi_domain_write_msg(irq_data, msg);
+}
@@ -35,4 +35,9 @@ void gic_cpu_config(void __iomem *base, void (*sync_access)(void));
void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
void *data);
+int gic_set_msi_addr(struct irq_data *data, struct msi_msg *msg);
+void gic_unset_msi_addr(struct irq_data *data);
+void gic_pci_msi_domain_write_msg(struct irq_data *irq_data,
+ struct msi_msg *msg);
+
#endif /* _IRQ_GIC_COMMON_H */
@@ -24,6 +24,7 @@
#include <linux/of_pci.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include "irq-gic-common.h"
/*
* MSI_TYPER:
@@ -83,7 +84,7 @@ static struct irq_chip gicv2m_msi_irq_chip = {
.irq_mask = gicv2m_mask_msi_irq,
.irq_unmask = gicv2m_unmask_msi_irq,
.irq_eoi = irq_chip_eoi_parent,
- .irq_write_msi_msg = pci_msi_domain_write_msg,
+ .irq_write_msi_msg = gic_pci_msi_domain_write_msg,
};
static struct msi_domain_info gicv2m_msi_domain_info = {
@@ -19,6 +19,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
+#include "irq-gic-common.h"
static void its_mask_msi_irq(struct irq_data *d)
{
@@ -37,7 +38,7 @@ static struct irq_chip its_msi_irq_chip = {
.irq_unmask = its_unmask_msi_irq,
.irq_mask = its_mask_msi_irq,
.irq_eoi = irq_chip_eoi_parent,
- .irq_write_msi_msg = pci_msi_domain_write_msg,
+ .irq_write_msi_msg = gic_pci_msi_domain_write_msg,
};
struct its_pci_alias {
In case the msi_desc references a device attached to an iommu domain, the msi address needs to be mapped in the IOMMU. Else any MSI write transaction will cause a fault. gic_set_msi_addr detects that case and allocates an iova bound to the physical address page comprising the MSI frame. This iova then is used as the msi_msg address. Unset operation decrements the reference on the binding. The functions are called in the irq_write_msi_msg ops implementation. At that time we can recognize whether the msi is setup or teared down looking at the msi_msg content. Indeed msi_domain_deactivate zeroes all the fields. Signed-off-by: Eric Auger <eric.auger@linaro.org> --- drivers/irqchip/irq-gic-common.c | 60 ++++++++++++++++++++++++++++++++ drivers/irqchip/irq-gic-common.h | 5 +++ drivers/irqchip/irq-gic-v2m.c | 3 +- drivers/irqchip/irq-gic-v3-its-pci-msi.c | 3 +- 4 files changed, 69 insertions(+), 2 deletions(-)