fsl-mc-msi.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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/of_irq.h>
  10. #include <linux/irq.h>
  11. #include <linux/irqdomain.h>
  12. #include <linux/msi.h>
  13. #include <linux/acpi_iort.h>
  14. #include "fsl-mc-private.h"
  15. #ifdef GENERIC_MSI_DOMAIN_OPS
  16. /*
  17. * Generate a unique ID identifying the interrupt (only used within the MSI
  18. * irqdomain. Combine the icid with the interrupt index.
  19. */
  20. static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev,
  21. struct msi_desc *desc)
  22. {
  23. /*
  24. * Make the base hwirq value for ICID*10000 so it is readable
  25. * as a decimal value in /proc/interrupts.
  26. */
  27. return (irq_hw_number_t)(desc->msi_index + (dev->icid * 10000));
  28. }
  29. static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg,
  30. struct msi_desc *desc)
  31. {
  32. arg->desc = desc;
  33. arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev),
  34. desc);
  35. }
  36. #else
  37. #define fsl_mc_msi_set_desc NULL
  38. #endif
  39. static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info)
  40. {
  41. struct msi_domain_ops *ops = info->ops;
  42. if (!ops)
  43. return;
  44. /*
  45. * set_desc should not be set by the caller
  46. */
  47. if (!ops->set_desc)
  48. ops->set_desc = fsl_mc_msi_set_desc;
  49. }
  50. static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev,
  51. struct fsl_mc_device_irq *mc_dev_irq,
  52. struct msi_desc *msi_desc)
  53. {
  54. int error;
  55. struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev;
  56. struct dprc_irq_cfg irq_cfg;
  57. /*
  58. * msi_desc->msg.address is 0x0 when this function is invoked in
  59. * the free_irq() code path. In this case, for the MC, we don't
  60. * really need to "unprogram" the MSI, so we just return.
  61. */
  62. if (msi_desc->msg.address_lo == 0x0 && msi_desc->msg.address_hi == 0x0)
  63. return;
  64. if (!owner_mc_dev)
  65. return;
  66. irq_cfg.paddr = ((u64)msi_desc->msg.address_hi << 32) |
  67. msi_desc->msg.address_lo;
  68. irq_cfg.val = msi_desc->msg.data;
  69. irq_cfg.irq_num = msi_desc->irq;
  70. if (owner_mc_dev == mc_bus_dev) {
  71. /*
  72. * IRQ is for the mc_bus_dev's DPRC itself
  73. */
  74. error = dprc_set_irq(mc_bus_dev->mc_io,
  75. MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
  76. mc_bus_dev->mc_handle,
  77. mc_dev_irq->dev_irq_index,
  78. &irq_cfg);
  79. if (error < 0) {
  80. dev_err(&owner_mc_dev->dev,
  81. "dprc_set_irq() failed: %d\n", error);
  82. }
  83. } else {
  84. /*
  85. * IRQ is for for a child device of mc_bus_dev
  86. */
  87. error = dprc_set_obj_irq(mc_bus_dev->mc_io,
  88. MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
  89. mc_bus_dev->mc_handle,
  90. owner_mc_dev->obj_desc.type,
  91. owner_mc_dev->obj_desc.id,
  92. mc_dev_irq->dev_irq_index,
  93. &irq_cfg);
  94. if (error < 0) {
  95. dev_err(&owner_mc_dev->dev,
  96. "dprc_obj_set_irq() failed: %d\n", error);
  97. }
  98. }
  99. }
  100. /*
  101. * NOTE: This function is invoked with interrupts disabled
  102. */
  103. static void fsl_mc_msi_write_msg(struct irq_data *irq_data,
  104. struct msi_msg *msg)
  105. {
  106. struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
  107. struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev);
  108. struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
  109. struct fsl_mc_device_irq *mc_dev_irq =
  110. &mc_bus->irq_resources[msi_desc->msi_index];
  111. msi_desc->msg = *msg;
  112. /*
  113. * Program the MSI (paddr, value) pair in the device:
  114. */
  115. __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq, msi_desc);
  116. }
  117. static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info)
  118. {
  119. struct irq_chip *chip = info->chip;
  120. if (!chip)
  121. return;
  122. /*
  123. * irq_write_msi_msg should not be set by the caller
  124. */
  125. if (!chip->irq_write_msi_msg)
  126. chip->irq_write_msi_msg = fsl_mc_msi_write_msg;
  127. }
  128. /**
  129. * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain
  130. * @fwnode: Optional firmware node of the interrupt controller
  131. * @info: MSI domain info
  132. * @parent: Parent irq domain
  133. *
  134. * Updates the domain and chip ops and creates a fsl-mc MSI
  135. * interrupt domain.
  136. *
  137. * Returns:
  138. * A domain pointer or NULL in case of failure.
  139. */
  140. struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode,
  141. struct msi_domain_info *info,
  142. struct irq_domain *parent)
  143. {
  144. struct irq_domain *domain;
  145. if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE)))
  146. info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
  147. if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
  148. fsl_mc_msi_update_dom_ops(info);
  149. if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
  150. fsl_mc_msi_update_chip_ops(info);
  151. info->flags |= MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS;
  152. domain = msi_create_irq_domain(fwnode, info, parent);
  153. if (domain)
  154. irq_domain_update_bus_token(domain, DOMAIN_BUS_FSL_MC_MSI);
  155. return domain;
  156. }
  157. struct irq_domain *fsl_mc_find_msi_domain(struct device *dev)
  158. {
  159. struct device *root_dprc_dev;
  160. struct device *bus_dev;
  161. struct irq_domain *msi_domain;
  162. struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
  163. fsl_mc_get_root_dprc(dev, &root_dprc_dev);
  164. bus_dev = root_dprc_dev->parent;
  165. if (bus_dev->of_node) {
  166. msi_domain = of_msi_map_get_device_domain(dev,
  167. mc_dev->icid,
  168. DOMAIN_BUS_FSL_MC_MSI);
  169. /*
  170. * if the msi-map property is missing assume that all the
  171. * child containers inherit the domain from the parent
  172. */
  173. if (!msi_domain)
  174. msi_domain = of_msi_get_domain(bus_dev,
  175. bus_dev->of_node,
  176. DOMAIN_BUS_FSL_MC_MSI);
  177. } else {
  178. msi_domain = iort_get_device_domain(dev, mc_dev->icid,
  179. DOMAIN_BUS_FSL_MC_MSI);
  180. }
  181. return msi_domain;
  182. }
  183. int fsl_mc_msi_domain_alloc_irqs(struct device *dev, unsigned int irq_count)
  184. {
  185. int error = msi_setup_device_data(dev);
  186. if (error)
  187. return error;
  188. /*
  189. * NOTE: Calling this function will trigger the invocation of the
  190. * its_fsl_mc_msi_prepare() callback
  191. */
  192. error = msi_domain_alloc_irqs_range(dev, MSI_DEFAULT_DOMAIN, 0, irq_count - 1);
  193. if (error)
  194. dev_err(dev, "Failed to allocate IRQs\n");
  195. return error;
  196. }
  197. void fsl_mc_msi_domain_free_irqs(struct device *dev)
  198. {
  199. msi_domain_free_irqs_all(dev, MSI_DEFAULT_DOMAIN);
  200. }