fsl-mc-msi.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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_device.h>
  10. #include <linux/of_address.h>
  11. #include <linux/of_irq.h>
  12. #include <linux/irq.h>
  13. #include <linux/irqdomain.h>
  14. #include <linux/msi.h>
  15. #include "fsl-mc-private.h"
  16. #ifdef GENERIC_MSI_DOMAIN_OPS
  17. /*
  18. * Generate a unique ID identifying the interrupt (only used within the MSI
  19. * irqdomain. Combine the icid with the interrupt index.
  20. */
  21. static irq_hw_number_t fsl_mc_domain_calc_hwirq(struct fsl_mc_device *dev,
  22. struct msi_desc *desc)
  23. {
  24. /*
  25. * Make the base hwirq value for ICID*10000 so it is readable
  26. * as a decimal value in /proc/interrupts.
  27. */
  28. return (irq_hw_number_t)(desc->fsl_mc.msi_index + (dev->icid * 10000));
  29. }
  30. static void fsl_mc_msi_set_desc(msi_alloc_info_t *arg,
  31. struct msi_desc *desc)
  32. {
  33. arg->desc = desc;
  34. arg->hwirq = fsl_mc_domain_calc_hwirq(to_fsl_mc_device(desc->dev),
  35. desc);
  36. }
  37. #else
  38. #define fsl_mc_msi_set_desc NULL
  39. #endif
  40. static void fsl_mc_msi_update_dom_ops(struct msi_domain_info *info)
  41. {
  42. struct msi_domain_ops *ops = info->ops;
  43. if (!ops)
  44. return;
  45. /*
  46. * set_desc should not be set by the caller
  47. */
  48. if (!ops->set_desc)
  49. ops->set_desc = fsl_mc_msi_set_desc;
  50. }
  51. static void __fsl_mc_msi_write_msg(struct fsl_mc_device *mc_bus_dev,
  52. struct fsl_mc_device_irq *mc_dev_irq)
  53. {
  54. int error;
  55. struct fsl_mc_device *owner_mc_dev = mc_dev_irq->mc_dev;
  56. struct msi_desc *msi_desc = mc_dev_irq->msi_desc;
  57. struct dprc_irq_cfg irq_cfg;
  58. /*
  59. * msi_desc->msg.address is 0x0 when this function is invoked in
  60. * the free_irq() code path. In this case, for the MC, we don't
  61. * really need to "unprogram" the MSI, so we just return.
  62. */
  63. if (msi_desc->msg.address_lo == 0x0 && msi_desc->msg.address_hi == 0x0)
  64. return;
  65. if (!owner_mc_dev)
  66. return;
  67. irq_cfg.paddr = ((u64)msi_desc->msg.address_hi << 32) |
  68. msi_desc->msg.address_lo;
  69. irq_cfg.val = msi_desc->msg.data;
  70. irq_cfg.irq_num = msi_desc->irq;
  71. if (owner_mc_dev == mc_bus_dev) {
  72. /*
  73. * IRQ is for the mc_bus_dev's DPRC itself
  74. */
  75. error = dprc_set_irq(mc_bus_dev->mc_io,
  76. MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
  77. mc_bus_dev->mc_handle,
  78. mc_dev_irq->dev_irq_index,
  79. &irq_cfg);
  80. if (error < 0) {
  81. dev_err(&owner_mc_dev->dev,
  82. "dprc_set_irq() failed: %d\n", error);
  83. }
  84. } else {
  85. /*
  86. * IRQ is for for a child device of mc_bus_dev
  87. */
  88. error = dprc_set_obj_irq(mc_bus_dev->mc_io,
  89. MC_CMD_FLAG_INTR_DIS | MC_CMD_FLAG_PRI,
  90. mc_bus_dev->mc_handle,
  91. owner_mc_dev->obj_desc.type,
  92. owner_mc_dev->obj_desc.id,
  93. mc_dev_irq->dev_irq_index,
  94. &irq_cfg);
  95. if (error < 0) {
  96. dev_err(&owner_mc_dev->dev,
  97. "dprc_obj_set_irq() failed: %d\n", error);
  98. }
  99. }
  100. }
  101. /*
  102. * NOTE: This function is invoked with interrupts disabled
  103. */
  104. static void fsl_mc_msi_write_msg(struct irq_data *irq_data,
  105. struct msi_msg *msg)
  106. {
  107. struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
  108. struct fsl_mc_device *mc_bus_dev = to_fsl_mc_device(msi_desc->dev);
  109. struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
  110. struct fsl_mc_device_irq *mc_dev_irq =
  111. &mc_bus->irq_resources[msi_desc->fsl_mc.msi_index];
  112. msi_desc->msg = *msg;
  113. /*
  114. * Program the MSI (paddr, value) pair in the device:
  115. */
  116. __fsl_mc_msi_write_msg(mc_bus_dev, mc_dev_irq);
  117. }
  118. static void fsl_mc_msi_update_chip_ops(struct msi_domain_info *info)
  119. {
  120. struct irq_chip *chip = info->chip;
  121. if (!chip)
  122. return;
  123. /*
  124. * irq_write_msi_msg should not be set by the caller
  125. */
  126. if (!chip->irq_write_msi_msg)
  127. chip->irq_write_msi_msg = fsl_mc_msi_write_msg;
  128. }
  129. /**
  130. * fsl_mc_msi_create_irq_domain - Create a fsl-mc MSI interrupt domain
  131. * @np: Optional device-tree node of the interrupt controller
  132. * @info: MSI domain info
  133. * @parent: Parent irq domain
  134. *
  135. * Updates the domain and chip ops and creates a fsl-mc MSI
  136. * interrupt domain.
  137. *
  138. * Returns:
  139. * A domain pointer or NULL in case of failure.
  140. */
  141. struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode,
  142. struct msi_domain_info *info,
  143. struct irq_domain *parent)
  144. {
  145. struct irq_domain *domain;
  146. if (WARN_ON((info->flags & MSI_FLAG_LEVEL_CAPABLE)))
  147. info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
  148. if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
  149. fsl_mc_msi_update_dom_ops(info);
  150. if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
  151. fsl_mc_msi_update_chip_ops(info);
  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. int fsl_mc_find_msi_domain(struct device *mc_platform_dev,
  158. struct irq_domain **mc_msi_domain)
  159. {
  160. struct irq_domain *msi_domain;
  161. struct device_node *mc_of_node = mc_platform_dev->of_node;
  162. msi_domain = of_msi_get_domain(mc_platform_dev, mc_of_node,
  163. DOMAIN_BUS_FSL_MC_MSI);
  164. if (!msi_domain) {
  165. pr_err("Unable to find fsl-mc MSI domain for %pOF\n",
  166. mc_of_node);
  167. return -ENOENT;
  168. }
  169. *mc_msi_domain = msi_domain;
  170. return 0;
  171. }
  172. static void fsl_mc_msi_free_descs(struct device *dev)
  173. {
  174. struct msi_desc *desc, *tmp;
  175. list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) {
  176. list_del(&desc->list);
  177. free_msi_entry(desc);
  178. }
  179. }
  180. static int fsl_mc_msi_alloc_descs(struct device *dev, unsigned int irq_count)
  181. {
  182. unsigned int i;
  183. int error;
  184. struct msi_desc *msi_desc;
  185. for (i = 0; i < irq_count; i++) {
  186. msi_desc = alloc_msi_entry(dev, 1, NULL);
  187. if (!msi_desc) {
  188. dev_err(dev, "Failed to allocate msi entry\n");
  189. error = -ENOMEM;
  190. goto cleanup_msi_descs;
  191. }
  192. msi_desc->fsl_mc.msi_index = i;
  193. INIT_LIST_HEAD(&msi_desc->list);
  194. list_add_tail(&msi_desc->list, dev_to_msi_list(dev));
  195. }
  196. return 0;
  197. cleanup_msi_descs:
  198. fsl_mc_msi_free_descs(dev);
  199. return error;
  200. }
  201. int fsl_mc_msi_domain_alloc_irqs(struct device *dev,
  202. unsigned int irq_count)
  203. {
  204. struct irq_domain *msi_domain;
  205. int error;
  206. if (!list_empty(dev_to_msi_list(dev)))
  207. return -EINVAL;
  208. error = fsl_mc_msi_alloc_descs(dev, irq_count);
  209. if (error < 0)
  210. return error;
  211. msi_domain = dev_get_msi_domain(dev);
  212. if (!msi_domain) {
  213. error = -EINVAL;
  214. goto cleanup_msi_descs;
  215. }
  216. /*
  217. * NOTE: Calling this function will trigger the invocation of the
  218. * its_fsl_mc_msi_prepare() callback
  219. */
  220. error = msi_domain_alloc_irqs(msi_domain, dev, irq_count);
  221. if (error) {
  222. dev_err(dev, "Failed to allocate IRQs\n");
  223. goto cleanup_msi_descs;
  224. }
  225. return 0;
  226. cleanup_msi_descs:
  227. fsl_mc_msi_free_descs(dev);
  228. return error;
  229. }
  230. void fsl_mc_msi_domain_free_irqs(struct device *dev)
  231. {
  232. struct irq_domain *msi_domain;
  233. msi_domain = dev_get_msi_domain(dev);
  234. if (!msi_domain)
  235. return;
  236. msi_domain_free_irqs(msi_domain, dev);
  237. if (list_empty(dev_to_msi_list(dev)))
  238. return;
  239. fsl_mc_msi_free_descs(dev);
  240. }