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

irqchip/gic-v2m: Add support for multiple MSI frames

The GICv2m driver is so far limited to a single MSI frame, but
nothing prevents an implementation from having several of them.

This patch expands the driver to enumerate all frames, keeping
the first one as the canonical identifier for the MSI domains.

Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Tested-by: Duc Dang <dhdang@apm.com>
Cc: <linux-arm-kernel@lists.infradead.org>
Cc: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Link: http://lkml.kernel.org/r/1444822037-16983-3-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>

authored by

Marc Zyngier and committed by
Thomas Gleixner
a71225e2 db8c70ec

+78 -46
+78 -46
drivers/irqchip/irq-gic-v2m.c
··· 50 50 /* List of flags for specific v2m implementation */ 51 51 #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 52 52 53 + static LIST_HEAD(v2m_nodes); 54 + static DEFINE_SPINLOCK(v2m_lock); 55 + 53 56 struct v2m_data { 54 - spinlock_t msi_cnt_lock; 57 + struct list_head entry; 58 + struct device_node *node; 55 59 struct resource res; /* GICv2m resource */ 56 60 void __iomem *base; /* GICv2m virt address */ 57 61 u32 spi_start; /* The SPI number that MSIs start */ ··· 162 158 return; 163 159 } 164 160 165 - spin_lock(&v2m->msi_cnt_lock); 161 + spin_lock(&v2m_lock); 166 162 __clear_bit(pos, v2m->bm); 167 - spin_unlock(&v2m->msi_cnt_lock); 163 + spin_unlock(&v2m_lock); 168 164 } 169 165 170 166 static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 171 167 unsigned int nr_irqs, void *args) 172 168 { 173 - struct v2m_data *v2m = domain->host_data; 169 + struct v2m_data *v2m = NULL, *tmp; 174 170 int hwirq, offset, err = 0; 175 171 176 - spin_lock(&v2m->msi_cnt_lock); 177 - offset = find_first_zero_bit(v2m->bm, v2m->nr_spis); 178 - if (offset < v2m->nr_spis) 179 - __set_bit(offset, v2m->bm); 180 - else 181 - err = -ENOSPC; 182 - spin_unlock(&v2m->msi_cnt_lock); 172 + spin_lock(&v2m_lock); 173 + list_for_each_entry(tmp, &v2m_nodes, entry) { 174 + offset = find_first_zero_bit(tmp->bm, tmp->nr_spis); 175 + if (offset < tmp->nr_spis) { 176 + __set_bit(offset, tmp->bm); 177 + v2m = tmp; 178 + break; 179 + } 180 + } 181 + spin_unlock(&v2m_lock); 183 182 184 - if (err) 185 - return err; 183 + if (!v2m) 184 + return -ENOSPC; 186 185 187 186 hwirq = v2m->spi_start + offset; 188 187 ··· 246 239 .chip = &gicv2m_pmsi_irq_chip, 247 240 }; 248 241 242 + static void gicv2m_teardown(void) 243 + { 244 + struct v2m_data *v2m, *tmp; 245 + 246 + list_for_each_entry_safe(v2m, tmp, &v2m_nodes, entry) { 247 + list_del(&v2m->entry); 248 + kfree(v2m->bm); 249 + iounmap(v2m->base); 250 + of_node_put(v2m->node); 251 + kfree(v2m); 252 + } 253 + } 254 + 255 + static int gicv2m_allocate_domains(struct irq_domain *parent) 256 + { 257 + struct irq_domain *inner_domain, *pci_domain, *plat_domain; 258 + struct v2m_data *v2m; 259 + 260 + v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry); 261 + if (!v2m) 262 + return 0; 263 + 264 + inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node), 265 + &gicv2m_domain_ops, v2m); 266 + if (!inner_domain) { 267 + pr_err("Failed to create GICv2m domain\n"); 268 + return -ENOMEM; 269 + } 270 + 271 + inner_domain->bus_token = DOMAIN_BUS_NEXUS; 272 + inner_domain->parent = parent; 273 + pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node), 274 + &gicv2m_msi_domain_info, 275 + inner_domain); 276 + plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node), 277 + &gicv2m_pmsi_domain_info, 278 + inner_domain); 279 + if (!pci_domain || !plat_domain) { 280 + pr_err("Failed to create MSI domains\n"); 281 + if (plat_domain) 282 + irq_domain_remove(plat_domain); 283 + if (pci_domain) 284 + irq_domain_remove(pci_domain); 285 + irq_domain_remove(inner_domain); 286 + return -ENOMEM; 287 + } 288 + 289 + return 0; 290 + } 291 + 249 292 static int __init gicv2m_init_one(struct device_node *node, 250 293 struct irq_domain *parent) 251 294 { 252 295 int ret; 253 296 struct v2m_data *v2m; 254 - struct irq_domain *inner_domain, *pci_domain, *plat_domain; 255 297 256 298 v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL); 257 299 if (!v2m) { 258 300 pr_err("Failed to allocate struct v2m_data.\n"); 259 301 return -ENOMEM; 260 302 } 303 + 304 + INIT_LIST_HEAD(&v2m->entry); 305 + v2m->node = node; 261 306 262 307 ret = of_address_to_resource(node, 0, &v2m->res); 263 308 if (ret) { ··· 358 299 goto err_iounmap; 359 300 } 360 301 361 - inner_domain = irq_domain_add_tree(node, &gicv2m_domain_ops, v2m); 362 - if (!inner_domain) { 363 - pr_err("Failed to create GICv2m domain\n"); 364 - ret = -ENOMEM; 365 - goto err_free_bm; 366 - } 367 - 368 - inner_domain->bus_token = DOMAIN_BUS_NEXUS; 369 - inner_domain->parent = parent; 370 - pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node), 371 - &gicv2m_msi_domain_info, 372 - inner_domain); 373 - plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node), 374 - &gicv2m_pmsi_domain_info, 375 - inner_domain); 376 - if (!pci_domain || !plat_domain) { 377 - pr_err("Failed to create MSI domains\n"); 378 - ret = -ENOMEM; 379 - goto err_free_domains; 380 - } 381 - 382 - spin_lock_init(&v2m->msi_cnt_lock); 383 - 302 + list_add_tail(&v2m->entry, &v2m_nodes); 384 303 pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, 385 304 (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, 386 305 v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); 387 306 388 307 return 0; 389 308 390 - err_free_domains: 391 - if (plat_domain) 392 - irq_domain_remove(plat_domain); 393 - if (pci_domain) 394 - irq_domain_remove(pci_domain); 395 - if (inner_domain) 396 - irq_domain_remove(inner_domain); 397 - err_free_bm: 398 - kfree(v2m->bm); 399 309 err_iounmap: 400 310 iounmap(v2m->base); 401 311 err_free_v2m: ··· 394 366 } 395 367 } 396 368 369 + if (!ret) 370 + ret = gicv2m_allocate_domains(parent); 371 + if (ret) 372 + gicv2m_teardown(); 397 373 return ret; 398 374 }