irq-gic-v3-its-fsl-mc-msi.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Freescale Management Complex (MC) bus driver MSI support
  4. *
  5. * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
  6. * Author: German Rivera <German.Rivera@freescale.com>
  7. *
  8. */
  9. #include <linux/acpi.h>
  10. #include <linux/acpi_iort.h>
  11. #include <linux/of_device.h>
  12. #include <linux/of_address.h>
  13. #include <linux/irq.h>
  14. #include <linux/msi.h>
  15. #include <linux/of.h>
  16. #include <linux/of_irq.h>
  17. #include <linux/fsl/mc.h>
  18. static struct irq_chip its_msi_irq_chip = {
  19. .name = "ITS-fMSI",
  20. .irq_mask = irq_chip_mask_parent,
  21. .irq_unmask = irq_chip_unmask_parent,
  22. .irq_eoi = irq_chip_eoi_parent,
  23. .irq_set_affinity = msi_domain_set_affinity
  24. };
  25. static u32 fsl_mc_msi_domain_get_msi_id(struct irq_domain *domain,
  26. struct fsl_mc_device *mc_dev)
  27. {
  28. struct device_node *of_node;
  29. u32 out_id;
  30. of_node = irq_domain_get_of_node(domain);
  31. out_id = of_node ? of_msi_map_id(&mc_dev->dev, of_node, mc_dev->icid) :
  32. iort_msi_map_id(&mc_dev->dev, mc_dev->icid);
  33. return out_id;
  34. }
  35. static int its_fsl_mc_msi_prepare(struct irq_domain *msi_domain,
  36. struct device *dev,
  37. int nvec, msi_alloc_info_t *info)
  38. {
  39. struct fsl_mc_device *mc_bus_dev;
  40. struct msi_domain_info *msi_info;
  41. if (!dev_is_fsl_mc(dev))
  42. return -EINVAL;
  43. mc_bus_dev = to_fsl_mc_device(dev);
  44. if (!(mc_bus_dev->flags & FSL_MC_IS_DPRC))
  45. return -EINVAL;
  46. /*
  47. * Set the device Id to be passed to the GIC-ITS:
  48. *
  49. * NOTE: This device id corresponds to the IOMMU stream ID
  50. * associated with the DPRC object (ICID).
  51. */
  52. info->scratchpad[0].ul = fsl_mc_msi_domain_get_msi_id(msi_domain,
  53. mc_bus_dev);
  54. msi_info = msi_get_domain_info(msi_domain->parent);
  55. /* Allocate at least 32 MSIs, and always as a power of 2 */
  56. nvec = max_t(int, 32, roundup_pow_of_two(nvec));
  57. return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
  58. }
  59. static struct msi_domain_ops its_fsl_mc_msi_ops __ro_after_init = {
  60. .msi_prepare = its_fsl_mc_msi_prepare,
  61. };
  62. static struct msi_domain_info its_fsl_mc_msi_domain_info = {
  63. .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
  64. .ops = &its_fsl_mc_msi_ops,
  65. .chip = &its_msi_irq_chip,
  66. };
  67. static const struct of_device_id its_device_id[] = {
  68. { .compatible = "arm,gic-v3-its", },
  69. {},
  70. };
  71. static void __init its_fsl_mc_msi_init_one(struct fwnode_handle *handle,
  72. const char *name)
  73. {
  74. struct irq_domain *parent;
  75. struct irq_domain *mc_msi_domain;
  76. parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
  77. if (!parent || !msi_get_domain_info(parent)) {
  78. pr_err("%s: unable to locate ITS domain\n", name);
  79. return;
  80. }
  81. mc_msi_domain = fsl_mc_msi_create_irq_domain(handle,
  82. &its_fsl_mc_msi_domain_info,
  83. parent);
  84. if (!mc_msi_domain) {
  85. pr_err("%s: unable to create fsl-mc domain\n", name);
  86. return;
  87. }
  88. pr_info("fsl-mc MSI: %s domain created\n", name);
  89. }
  90. #ifdef CONFIG_ACPI
  91. static int __init
  92. its_fsl_mc_msi_parse_madt(union acpi_subtable_headers *header,
  93. const unsigned long end)
  94. {
  95. struct acpi_madt_generic_translator *its_entry;
  96. struct fwnode_handle *dom_handle;
  97. const char *node_name;
  98. int err = 0;
  99. its_entry = (struct acpi_madt_generic_translator *)header;
  100. node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
  101. (long)its_entry->base_address);
  102. dom_handle = iort_find_domain_token(its_entry->translation_id);
  103. if (!dom_handle) {
  104. pr_err("%s: Unable to locate ITS domain handle\n", node_name);
  105. err = -ENXIO;
  106. goto out;
  107. }
  108. its_fsl_mc_msi_init_one(dom_handle, node_name);
  109. out:
  110. kfree(node_name);
  111. return err;
  112. }
  113. static void __init its_fsl_mc_acpi_msi_init(void)
  114. {
  115. acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
  116. its_fsl_mc_msi_parse_madt, 0);
  117. }
  118. #else
  119. static inline void its_fsl_mc_acpi_msi_init(void) { }
  120. #endif
  121. static void __init its_fsl_mc_of_msi_init(void)
  122. {
  123. struct device_node *np;
  124. for (np = of_find_matching_node(NULL, its_device_id); np;
  125. np = of_find_matching_node(np, its_device_id)) {
  126. if (!of_device_is_available(np))
  127. continue;
  128. if (!of_property_read_bool(np, "msi-controller"))
  129. continue;
  130. its_fsl_mc_msi_init_one(of_node_to_fwnode(np),
  131. np->full_name);
  132. }
  133. }
  134. static int __init its_fsl_mc_msi_init(void)
  135. {
  136. its_fsl_mc_of_msi_init();
  137. its_fsl_mc_acpi_msi_init();
  138. return 0;
  139. }
  140. early_initcall(its_fsl_mc_msi_init);