Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip/gic-v3: Add PCI/MSI support to the GICv3 MBI sub-driver

You would hope that if you have a GICv3 in your system, you'd use the ITS,
as it provides a large interrupt ID space and device isolation. Sadly,
some SoC integrations are less than perfect, and the ITS is not usesable on
those.

The only solution for these systems is to use the MBI interface, and
rely on a very small number of possible vectors.

This patch thus adds minimal support for PCI/MSI on top of the GICv3
MBI driver. Please don't use it if you can avoid it.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Rob Herring <robh@kernel.org>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
Cc: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lkml.kernel.org/r/20180508121438.11301-9-marc.zyngier@arm.com

authored by

Marc Zyngier and committed by
Thomas Gleixner
38985351 50528752

+59 -3
+59 -3
drivers/irqchip/irq-gic-v3-mbi.c
··· 12 12 #include <linux/kernel.h> 13 13 #include <linux/msi.h> 14 14 #include <linux/of_address.h> 15 + #include <linux/of_pci.h> 15 16 #include <linux/slab.h> 16 17 #include <linux/spinlock.h> 17 18 ··· 145 144 iommu_dma_map_msi_msg(data->irq, msg); 146 145 } 147 146 147 + #ifdef CONFIG_PCI_MSI 148 + /* PCI-specific irqchip */ 149 + static void mbi_mask_msi_irq(struct irq_data *d) 150 + { 151 + pci_msi_mask_irq(d); 152 + irq_chip_mask_parent(d); 153 + } 154 + 155 + static void mbi_unmask_msi_irq(struct irq_data *d) 156 + { 157 + pci_msi_unmask_irq(d); 158 + irq_chip_unmask_parent(d); 159 + } 160 + 161 + static struct irq_chip mbi_msi_irq_chip = { 162 + .name = "MSI", 163 + .irq_mask = mbi_mask_msi_irq, 164 + .irq_unmask = mbi_unmask_msi_irq, 165 + .irq_eoi = irq_chip_eoi_parent, 166 + .irq_compose_msi_msg = mbi_compose_msi_msg, 167 + .irq_write_msi_msg = pci_msi_domain_write_msg, 168 + }; 169 + 170 + static struct msi_domain_info mbi_msi_domain_info = { 171 + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | 172 + MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI), 173 + .chip = &mbi_msi_irq_chip, 174 + }; 175 + 176 + static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, 177 + struct irq_domain **pci_domain) 178 + { 179 + *pci_domain = pci_msi_create_irq_domain(nexus_domain->parent->fwnode, 180 + &mbi_msi_domain_info, 181 + nexus_domain); 182 + if (!*pci_domain) 183 + return -ENOMEM; 184 + 185 + return 0; 186 + } 187 + #else 188 + static int mbi_allocate_pci_domain(struct irq_domain *nexus_domain, 189 + struct irq_domain **pci_domain) 190 + { 191 + *pci_domain = NULL; 192 + return 0; 193 + } 194 + #endif 195 + 148 196 static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) 149 197 { 150 198 mbi_compose_msi_msg(data, msg); ··· 225 175 226 176 static int mbi_allocate_domains(struct irq_domain *parent) 227 177 { 228 - struct irq_domain *nexus_domain, *plat_domain; 178 + struct irq_domain *nexus_domain, *pci_domain, *plat_domain; 179 + int err; 229 180 230 181 nexus_domain = irq_domain_create_tree(parent->fwnode, 231 182 &mbi_domain_ops, NULL); ··· 236 185 irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS); 237 186 nexus_domain->parent = parent; 238 187 188 + err = mbi_allocate_pci_domain(nexus_domain, &pci_domain); 189 + 239 190 plat_domain = platform_msi_create_irq_domain(parent->fwnode, 240 191 &mbi_pmsi_domain_info, 241 192 nexus_domain); 242 193 243 - if (!plat_domain) { 244 - irq_domain_remove(plat_domain); 194 + if (err || !plat_domain) { 195 + if (plat_domain) 196 + irq_domain_remove(plat_domain); 197 + if (pci_domain) 198 + irq_domain_remove(pci_domain); 245 199 irq_domain_remove(nexus_domain); 246 200 return -ENOMEM; 247 201 }