root/drivers/irqchip/irq-gic-v2m.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gicv2m_mask_msi_irq
  2. gicv2m_unmask_msi_irq
  3. gicv2m_get_msi_addr
  4. gicv2m_compose_msi_msg
  5. gicv2m_irq_gic_domain_alloc
  6. gicv2m_unalloc_msi
  7. gicv2m_irq_domain_alloc
  8. gicv2m_irq_domain_free
  9. is_msi_spi_valid
  10. gicv2m_teardown
  11. gicv2m_allocate_domains
  12. gicv2m_init_one
  13. gicv2m_of_init
  14. gicv2m_get_fwnode
  15. acpi_check_amazon_graviton_quirks
  16. acpi_parse_madt_msi
  17. gicv2m_acpi_init
  18. gicv2m_acpi_init
  19. gicv2m_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * ARM GIC v2m MSI(-X) support
   4  * Support for Message Signaled Interrupts for systems that
   5  * implement ARM Generic Interrupt Controller: GICv2m.
   6  *
   7  * Copyright (C) 2014 Advanced Micro Devices, Inc.
   8  * Authors: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
   9  *          Harish Kasiviswanathan <harish.kasiviswanathan@amd.com>
  10  *          Brandon Anderson <brandon.anderson@amd.com>
  11  */
  12 
  13 #define pr_fmt(fmt) "GICv2m: " fmt
  14 
  15 #include <linux/acpi.h>
  16 #include <linux/dma-iommu.h>
  17 #include <linux/irq.h>
  18 #include <linux/irqdomain.h>
  19 #include <linux/kernel.h>
  20 #include <linux/msi.h>
  21 #include <linux/of_address.h>
  22 #include <linux/of_pci.h>
  23 #include <linux/slab.h>
  24 #include <linux/spinlock.h>
  25 #include <linux/irqchip/arm-gic.h>
  26 
  27 /*
  28 * MSI_TYPER:
  29 *     [31:26] Reserved
  30 *     [25:16] lowest SPI assigned to MSI
  31 *     [15:10] Reserved
  32 *     [9:0]   Numer of SPIs assigned to MSI
  33 */
  34 #define V2M_MSI_TYPER                  0x008
  35 #define V2M_MSI_TYPER_BASE_SHIFT       16
  36 #define V2M_MSI_TYPER_BASE_MASK        0x3FF
  37 #define V2M_MSI_TYPER_NUM_MASK         0x3FF
  38 #define V2M_MSI_SETSPI_NS              0x040
  39 #define V2M_MIN_SPI                    32
  40 #define V2M_MAX_SPI                    1019
  41 #define V2M_MSI_IIDR                   0xFCC
  42 
  43 #define V2M_MSI_TYPER_BASE_SPI(x)      \
  44                (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK)
  45 
  46 #define V2M_MSI_TYPER_NUM_SPI(x)       ((x) & V2M_MSI_TYPER_NUM_MASK)
  47 
  48 /* APM X-Gene with GICv2m MSI_IIDR register value */
  49 #define XGENE_GICV2M_MSI_IIDR           0x06000170
  50 
  51 /* Broadcom NS2 GICv2m MSI_IIDR register value */
  52 #define BCM_NS2_GICV2M_MSI_IIDR         0x0000013f
  53 
  54 /* List of flags for specific v2m implementation */
  55 #define GICV2M_NEEDS_SPI_OFFSET         0x00000001
  56 #define GICV2M_GRAVITON_ADDRESS_ONLY    0x00000002
  57 
  58 static LIST_HEAD(v2m_nodes);
  59 static DEFINE_SPINLOCK(v2m_lock);
  60 
  61 struct v2m_data {
  62         struct list_head entry;
  63         struct fwnode_handle *fwnode;
  64         struct resource res;    /* GICv2m resource */
  65         void __iomem *base;     /* GICv2m virt address */
  66         u32 spi_start;          /* The SPI number that MSIs start */
  67         u32 nr_spis;            /* The number of SPIs for MSIs */
  68         u32 spi_offset;         /* offset to be subtracted from SPI number */
  69         unsigned long *bm;      /* MSI vector bitmap */
  70         u32 flags;              /* v2m flags for specific implementation */
  71 };
  72 
  73 static void gicv2m_mask_msi_irq(struct irq_data *d)
  74 {
  75         pci_msi_mask_irq(d);
  76         irq_chip_mask_parent(d);
  77 }
  78 
  79 static void gicv2m_unmask_msi_irq(struct irq_data *d)
  80 {
  81         pci_msi_unmask_irq(d);
  82         irq_chip_unmask_parent(d);
  83 }
  84 
  85 static struct irq_chip gicv2m_msi_irq_chip = {
  86         .name                   = "MSI",
  87         .irq_mask               = gicv2m_mask_msi_irq,
  88         .irq_unmask             = gicv2m_unmask_msi_irq,
  89         .irq_eoi                = irq_chip_eoi_parent,
  90         .irq_write_msi_msg      = pci_msi_domain_write_msg,
  91 };
  92 
  93 static struct msi_domain_info gicv2m_msi_domain_info = {
  94         .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
  95                    MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
  96         .chip   = &gicv2m_msi_irq_chip,
  97 };
  98 
  99 static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq)
 100 {
 101         if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
 102                 return v2m->res.start | ((hwirq - 32) << 3);
 103         else
 104                 return v2m->res.start + V2M_MSI_SETSPI_NS;
 105 }
 106 
 107 static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
 108 {
 109         struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
 110         phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq);
 111 
 112         msg->address_hi = upper_32_bits(addr);
 113         msg->address_lo = lower_32_bits(addr);
 114 
 115         if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
 116                 msg->data = 0;
 117         else
 118                 msg->data = data->hwirq;
 119         if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
 120                 msg->data -= v2m->spi_offset;
 121 
 122         iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
 123 }
 124 
 125 static struct irq_chip gicv2m_irq_chip = {
 126         .name                   = "GICv2m",
 127         .irq_mask               = irq_chip_mask_parent,
 128         .irq_unmask             = irq_chip_unmask_parent,
 129         .irq_eoi                = irq_chip_eoi_parent,
 130         .irq_set_affinity       = irq_chip_set_affinity_parent,
 131         .irq_compose_msi_msg    = gicv2m_compose_msi_msg,
 132 };
 133 
 134 static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
 135                                        unsigned int virq,
 136                                        irq_hw_number_t hwirq)
 137 {
 138         struct irq_fwspec fwspec;
 139         struct irq_data *d;
 140         int err;
 141 
 142         if (is_of_node(domain->parent->fwnode)) {
 143                 fwspec.fwnode = domain->parent->fwnode;
 144                 fwspec.param_count = 3;
 145                 fwspec.param[0] = 0;
 146                 fwspec.param[1] = hwirq - 32;
 147                 fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
 148         } else if (is_fwnode_irqchip(domain->parent->fwnode)) {
 149                 fwspec.fwnode = domain->parent->fwnode;
 150                 fwspec.param_count = 2;
 151                 fwspec.param[0] = hwirq;
 152                 fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
 153         } else {
 154                 return -EINVAL;
 155         }
 156 
 157         err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
 158         if (err)
 159                 return err;
 160 
 161         /* Configure the interrupt line to be edge */
 162         d = irq_domain_get_irq_data(domain->parent, virq);
 163         d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
 164         return 0;
 165 }
 166 
 167 static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq,
 168                                int nr_irqs)
 169 {
 170         spin_lock(&v2m_lock);
 171         bitmap_release_region(v2m->bm, hwirq - v2m->spi_start,
 172                               get_count_order(nr_irqs));
 173         spin_unlock(&v2m_lock);
 174 }
 175 
 176 static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 177                                    unsigned int nr_irqs, void *args)
 178 {
 179         msi_alloc_info_t *info = args;
 180         struct v2m_data *v2m = NULL, *tmp;
 181         int hwirq, offset, i, err = 0;
 182 
 183         spin_lock(&v2m_lock);
 184         list_for_each_entry(tmp, &v2m_nodes, entry) {
 185                 offset = bitmap_find_free_region(tmp->bm, tmp->nr_spis,
 186                                                  get_count_order(nr_irqs));
 187                 if (offset >= 0) {
 188                         v2m = tmp;
 189                         break;
 190                 }
 191         }
 192         spin_unlock(&v2m_lock);
 193 
 194         if (!v2m)
 195                 return -ENOSPC;
 196 
 197         hwirq = v2m->spi_start + offset;
 198 
 199         err = iommu_dma_prepare_msi(info->desc,
 200                                     gicv2m_get_msi_addr(v2m, hwirq));
 201         if (err)
 202                 return err;
 203 
 204         for (i = 0; i < nr_irqs; i++) {
 205                 err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
 206                 if (err)
 207                         goto fail;
 208 
 209                 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
 210                                               &gicv2m_irq_chip, v2m);
 211         }
 212 
 213         return 0;
 214 
 215 fail:
 216         irq_domain_free_irqs_parent(domain, virq, nr_irqs);
 217         gicv2m_unalloc_msi(v2m, hwirq, nr_irqs);
 218         return err;
 219 }
 220 
 221 static void gicv2m_irq_domain_free(struct irq_domain *domain,
 222                                    unsigned int virq, unsigned int nr_irqs)
 223 {
 224         struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 225         struct v2m_data *v2m = irq_data_get_irq_chip_data(d);
 226 
 227         gicv2m_unalloc_msi(v2m, d->hwirq, nr_irqs);
 228         irq_domain_free_irqs_parent(domain, virq, nr_irqs);
 229 }
 230 
 231 static const struct irq_domain_ops gicv2m_domain_ops = {
 232         .alloc                  = gicv2m_irq_domain_alloc,
 233         .free                   = gicv2m_irq_domain_free,
 234 };
 235 
 236 static bool is_msi_spi_valid(u32 base, u32 num)
 237 {
 238         if (base < V2M_MIN_SPI) {
 239                 pr_err("Invalid MSI base SPI (base:%u)\n", base);
 240                 return false;
 241         }
 242 
 243         if ((num == 0) || (base + num > V2M_MAX_SPI)) {
 244                 pr_err("Number of SPIs (%u) exceed maximum (%u)\n",
 245                        num, V2M_MAX_SPI - V2M_MIN_SPI + 1);
 246                 return false;
 247         }
 248 
 249         return true;
 250 }
 251 
 252 static struct irq_chip gicv2m_pmsi_irq_chip = {
 253         .name                   = "pMSI",
 254 };
 255 
 256 static struct msi_domain_ops gicv2m_pmsi_ops = {
 257 };
 258 
 259 static struct msi_domain_info gicv2m_pmsi_domain_info = {
 260         .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
 261         .ops    = &gicv2m_pmsi_ops,
 262         .chip   = &gicv2m_pmsi_irq_chip,
 263 };
 264 
 265 static void gicv2m_teardown(void)
 266 {
 267         struct v2m_data *v2m, *tmp;
 268 
 269         list_for_each_entry_safe(v2m, tmp, &v2m_nodes, entry) {
 270                 list_del(&v2m->entry);
 271                 kfree(v2m->bm);
 272                 iounmap(v2m->base);
 273                 of_node_put(to_of_node(v2m->fwnode));
 274                 if (is_fwnode_irqchip(v2m->fwnode))
 275                         irq_domain_free_fwnode(v2m->fwnode);
 276                 kfree(v2m);
 277         }
 278 }
 279 
 280 static int gicv2m_allocate_domains(struct irq_domain *parent)
 281 {
 282         struct irq_domain *inner_domain, *pci_domain, *plat_domain;
 283         struct v2m_data *v2m;
 284 
 285         v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
 286         if (!v2m)
 287                 return 0;
 288 
 289         inner_domain = irq_domain_create_tree(v2m->fwnode,
 290                                               &gicv2m_domain_ops, v2m);
 291         if (!inner_domain) {
 292                 pr_err("Failed to create GICv2m domain\n");
 293                 return -ENOMEM;
 294         }
 295 
 296         irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
 297         inner_domain->parent = parent;
 298         pci_domain = pci_msi_create_irq_domain(v2m->fwnode,
 299                                                &gicv2m_msi_domain_info,
 300                                                inner_domain);
 301         plat_domain = platform_msi_create_irq_domain(v2m->fwnode,
 302                                                      &gicv2m_pmsi_domain_info,
 303                                                      inner_domain);
 304         if (!pci_domain || !plat_domain) {
 305                 pr_err("Failed to create MSI domains\n");
 306                 if (plat_domain)
 307                         irq_domain_remove(plat_domain);
 308                 if (pci_domain)
 309                         irq_domain_remove(pci_domain);
 310                 irq_domain_remove(inner_domain);
 311                 return -ENOMEM;
 312         }
 313 
 314         return 0;
 315 }
 316 
 317 static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
 318                                   u32 spi_start, u32 nr_spis,
 319                                   struct resource *res, u32 flags)
 320 {
 321         int ret;
 322         struct v2m_data *v2m;
 323 
 324         v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL);
 325         if (!v2m) {
 326                 pr_err("Failed to allocate struct v2m_data.\n");
 327                 return -ENOMEM;
 328         }
 329 
 330         INIT_LIST_HEAD(&v2m->entry);
 331         v2m->fwnode = fwnode;
 332         v2m->flags = flags;
 333 
 334         memcpy(&v2m->res, res, sizeof(struct resource));
 335 
 336         v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res));
 337         if (!v2m->base) {
 338                 pr_err("Failed to map GICv2m resource\n");
 339                 ret = -ENOMEM;
 340                 goto err_free_v2m;
 341         }
 342 
 343         if (spi_start && nr_spis) {
 344                 v2m->spi_start = spi_start;
 345                 v2m->nr_spis = nr_spis;
 346         } else {
 347                 u32 typer;
 348 
 349                 /* Graviton should always have explicit spi_start/nr_spis */
 350                 if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) {
 351                         ret = -EINVAL;
 352                         goto err_iounmap;
 353                 }
 354                 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
 355 
 356                 v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer);
 357                 v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer);
 358         }
 359 
 360         if (!is_msi_spi_valid(v2m->spi_start, v2m->nr_spis)) {
 361                 ret = -EINVAL;
 362                 goto err_iounmap;
 363         }
 364 
 365         /*
 366          * APM X-Gene GICv2m implementation has an erratum where
 367          * the MSI data needs to be the offset from the spi_start
 368          * in order to trigger the correct MSI interrupt. This is
 369          * different from the standard GICv2m implementation where
 370          * the MSI data is the absolute value within the range from
 371          * spi_start to (spi_start + num_spis).
 372          *
 373          * Broadom NS2 GICv2m implementation has an erratum where the MSI data
 374          * is 'spi_number - 32'
 375          *
 376          * Reading that register fails on the Graviton implementation
 377          */
 378         if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) {
 379                 switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
 380                 case XGENE_GICV2M_MSI_IIDR:
 381                         v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
 382                         v2m->spi_offset = v2m->spi_start;
 383                         break;
 384                 case BCM_NS2_GICV2M_MSI_IIDR:
 385                         v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
 386                         v2m->spi_offset = 32;
 387                         break;
 388                 }
 389         }
 390         v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long),
 391                           GFP_KERNEL);
 392         if (!v2m->bm) {
 393                 ret = -ENOMEM;
 394                 goto err_iounmap;
 395         }
 396 
 397         list_add_tail(&v2m->entry, &v2m_nodes);
 398 
 399         pr_info("range%pR, SPI[%d:%d]\n", res,
 400                 v2m->spi_start, (v2m->spi_start + v2m->nr_spis - 1));
 401         return 0;
 402 
 403 err_iounmap:
 404         iounmap(v2m->base);
 405 err_free_v2m:
 406         kfree(v2m);
 407         return ret;
 408 }
 409 
 410 static struct of_device_id gicv2m_device_id[] = {
 411         {       .compatible     = "arm,gic-v2m-frame",  },
 412         {},
 413 };
 414 
 415 static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
 416                                  struct irq_domain *parent)
 417 {
 418         int ret = 0;
 419         struct device_node *node = to_of_node(parent_handle);
 420         struct device_node *child;
 421 
 422         for (child = of_find_matching_node(node, gicv2m_device_id); child;
 423              child = of_find_matching_node(child, gicv2m_device_id)) {
 424                 u32 spi_start = 0, nr_spis = 0;
 425                 struct resource res;
 426 
 427                 if (!of_find_property(child, "msi-controller", NULL))
 428                         continue;
 429 
 430                 ret = of_address_to_resource(child, 0, &res);
 431                 if (ret) {
 432                         pr_err("Failed to allocate v2m resource.\n");
 433                         break;
 434                 }
 435 
 436                 if (!of_property_read_u32(child, "arm,msi-base-spi",
 437                                           &spi_start) &&
 438                     !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis))
 439                         pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
 440                                 spi_start, nr_spis);
 441 
 442                 ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis,
 443                                       &res, 0);
 444                 if (ret) {
 445                         of_node_put(child);
 446                         break;
 447                 }
 448         }
 449 
 450         if (!ret)
 451                 ret = gicv2m_allocate_domains(parent);
 452         if (ret)
 453                 gicv2m_teardown();
 454         return ret;
 455 }
 456 
 457 #ifdef CONFIG_ACPI
 458 static int acpi_num_msi;
 459 
 460 static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
 461 {
 462         struct v2m_data *data;
 463 
 464         if (WARN_ON(acpi_num_msi <= 0))
 465                 return NULL;
 466 
 467         /* We only return the fwnode of the first MSI frame. */
 468         data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
 469         if (!data)
 470                 return NULL;
 471 
 472         return data->fwnode;
 473 }
 474 
 475 static bool acpi_check_amazon_graviton_quirks(void)
 476 {
 477         static struct acpi_table_madt *madt;
 478         acpi_status status;
 479         bool rc = false;
 480 
 481 #define ACPI_AMZN_OEM_ID                "AMAZON"
 482 
 483         status = acpi_get_table(ACPI_SIG_MADT, 0,
 484                                 (struct acpi_table_header **)&madt);
 485 
 486         if (ACPI_FAILURE(status) || !madt)
 487                 return rc;
 488         rc = !memcmp(madt->header.oem_id, ACPI_AMZN_OEM_ID, ACPI_OEM_ID_SIZE);
 489         acpi_put_table((struct acpi_table_header *)madt);
 490 
 491         return rc;
 492 }
 493 
 494 static int __init
 495 acpi_parse_madt_msi(union acpi_subtable_headers *header,
 496                     const unsigned long end)
 497 {
 498         int ret;
 499         struct resource res;
 500         u32 spi_start = 0, nr_spis = 0;
 501         struct acpi_madt_generic_msi_frame *m;
 502         struct fwnode_handle *fwnode;
 503         u32 flags = 0;
 504 
 505         m = (struct acpi_madt_generic_msi_frame *)header;
 506         if (BAD_MADT_ENTRY(m, end))
 507                 return -EINVAL;
 508 
 509         res.start = m->base_address;
 510         res.end = m->base_address + SZ_4K - 1;
 511         res.flags = IORESOURCE_MEM;
 512 
 513         if (acpi_check_amazon_graviton_quirks()) {
 514                 pr_info("applying Amazon Graviton quirk\n");
 515                 res.end = res.start + SZ_8K - 1;
 516                 flags |= GICV2M_GRAVITON_ADDRESS_ONLY;
 517                 gicv2m_msi_domain_info.flags &= ~MSI_FLAG_MULTI_PCI_MSI;
 518         }
 519 
 520         if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
 521                 spi_start = m->spi_base;
 522                 nr_spis = m->spi_count;
 523 
 524                 pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
 525                         spi_start, nr_spis);
 526         }
 527 
 528         fwnode = irq_domain_alloc_fwnode(&res.start);
 529         if (!fwnode) {
 530                 pr_err("Unable to allocate GICv2m domain token\n");
 531                 return -EINVAL;
 532         }
 533 
 534         ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res, flags);
 535         if (ret)
 536                 irq_domain_free_fwnode(fwnode);
 537 
 538         return ret;
 539 }
 540 
 541 static int __init gicv2m_acpi_init(struct irq_domain *parent)
 542 {
 543         int ret;
 544 
 545         if (acpi_num_msi > 0)
 546                 return 0;
 547 
 548         acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME,
 549                                       acpi_parse_madt_msi, 0);
 550 
 551         if (acpi_num_msi <= 0)
 552                 goto err_out;
 553 
 554         ret = gicv2m_allocate_domains(parent);
 555         if (ret)
 556                 goto err_out;
 557 
 558         pci_msi_register_fwnode_provider(&gicv2m_get_fwnode);
 559 
 560         return 0;
 561 
 562 err_out:
 563         gicv2m_teardown();
 564         return -EINVAL;
 565 }
 566 #else /* CONFIG_ACPI */
 567 static int __init gicv2m_acpi_init(struct irq_domain *parent)
 568 {
 569         return -EINVAL;
 570 }
 571 #endif /* CONFIG_ACPI */
 572 
 573 int __init gicv2m_init(struct fwnode_handle *parent_handle,
 574                        struct irq_domain *parent)
 575 {
 576         if (is_of_node(parent_handle))
 577                 return gicv2m_of_init(parent_handle, parent);
 578 
 579         return gicv2m_acpi_init(parent);
 580 }

/* [<][>][^][v][top][bottom][index][help] */