irq-ti-sci-intr.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Texas Instruments' K3 Interrupt Router irqchip driver
  4. *
  5. * Copyright (C) 2018-2019 Texas Instruments Incorporated - https://www.ti.com/
  6. * Lokesh Vutla <lokeshvutla@ti.com>
  7. */
  8. #include <linux/err.h>
  9. #include <linux/module.h>
  10. #include <linux/moduleparam.h>
  11. #include <linux/io.h>
  12. #include <linux/irqchip.h>
  13. #include <linux/irqdomain.h>
  14. #include <linux/of_platform.h>
  15. #include <linux/of_address.h>
  16. #include <linux/of_irq.h>
  17. #include <linux/soc/ti/ti_sci_protocol.h>
  18. /**
  19. * struct ti_sci_intr_irq_domain - Structure representing a TISCI based
  20. * Interrupt Router IRQ domain.
  21. * @sci: Pointer to TISCI handle
  22. * @out_irqs: TISCI resource pointer representing INTR irqs.
  23. * @dev: Struct device pointer.
  24. * @ti_sci_id: TI-SCI device identifier
  25. * @type: Specifies the trigger type supported by this Interrupt Router
  26. */
  27. struct ti_sci_intr_irq_domain {
  28. const struct ti_sci_handle *sci;
  29. struct ti_sci_resource *out_irqs;
  30. struct device *dev;
  31. u32 ti_sci_id;
  32. u32 type;
  33. };
  34. static struct irq_chip ti_sci_intr_irq_chip = {
  35. .name = "INTR",
  36. .irq_eoi = irq_chip_eoi_parent,
  37. .irq_mask = irq_chip_mask_parent,
  38. .irq_unmask = irq_chip_unmask_parent,
  39. .irq_set_type = irq_chip_set_type_parent,
  40. .irq_retrigger = irq_chip_retrigger_hierarchy,
  41. .irq_set_affinity = irq_chip_set_affinity_parent,
  42. };
  43. /**
  44. * ti_sci_intr_irq_domain_translate() - Retrieve hwirq and type from
  45. * IRQ firmware specific handler.
  46. * @domain: Pointer to IRQ domain
  47. * @fwspec: Pointer to IRQ specific firmware structure
  48. * @hwirq: IRQ number identified by hardware
  49. * @type: IRQ type
  50. *
  51. * Return 0 if all went ok else appropriate error.
  52. */
  53. static int ti_sci_intr_irq_domain_translate(struct irq_domain *domain,
  54. struct irq_fwspec *fwspec,
  55. unsigned long *hwirq,
  56. unsigned int *type)
  57. {
  58. struct ti_sci_intr_irq_domain *intr = domain->host_data;
  59. if (fwspec->param_count != 1)
  60. return -EINVAL;
  61. *hwirq = fwspec->param[0];
  62. *type = intr->type;
  63. return 0;
  64. }
  65. /**
  66. * ti_sci_intr_xlate_irq() - Translate hwirq to parent's hwirq.
  67. * @intr: IRQ domain corresponding to Interrupt Router
  68. * @irq: Hardware irq corresponding to the above irq domain
  69. *
  70. * Return parent irq number if translation is available else -ENOENT.
  71. */
  72. static int ti_sci_intr_xlate_irq(struct ti_sci_intr_irq_domain *intr, u32 irq)
  73. {
  74. struct device_node *np = dev_of_node(intr->dev);
  75. u32 base, pbase, size, len;
  76. const __be32 *range;
  77. range = of_get_property(np, "ti,interrupt-ranges", &len);
  78. if (!range)
  79. return irq;
  80. for (len /= sizeof(*range); len >= 3; len -= 3) {
  81. base = be32_to_cpu(*range++);
  82. pbase = be32_to_cpu(*range++);
  83. size = be32_to_cpu(*range++);
  84. if (base <= irq && irq < base + size)
  85. return irq - base + pbase;
  86. }
  87. return -ENOENT;
  88. }
  89. /**
  90. * ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain.
  91. * @domain: Domain to which the irqs belong
  92. * @virq: Linux virtual IRQ to be freed.
  93. * @nr_irqs: Number of continuous irqs to be freed
  94. */
  95. static void ti_sci_intr_irq_domain_free(struct irq_domain *domain,
  96. unsigned int virq, unsigned int nr_irqs)
  97. {
  98. struct ti_sci_intr_irq_domain *intr = domain->host_data;
  99. struct irq_data *data;
  100. int out_irq;
  101. data = irq_domain_get_irq_data(domain, virq);
  102. out_irq = (uintptr_t)data->chip_data;
  103. intr->sci->ops.rm_irq_ops.free_irq(intr->sci,
  104. intr->ti_sci_id, data->hwirq,
  105. intr->ti_sci_id, out_irq);
  106. ti_sci_release_resource(intr->out_irqs, out_irq);
  107. irq_domain_free_irqs_parent(domain, virq, 1);
  108. irq_domain_reset_irq_data(data);
  109. }
  110. /**
  111. * ti_sci_intr_alloc_parent_irq() - Allocate parent IRQ
  112. * @domain: Pointer to the interrupt router IRQ domain
  113. * @virq: Corresponding Linux virtual IRQ number
  114. * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain
  115. *
  116. * Returns intr output irq if all went well else appropriate error pointer.
  117. */
  118. static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain,
  119. unsigned int virq, u32 hwirq)
  120. {
  121. struct ti_sci_intr_irq_domain *intr = domain->host_data;
  122. struct device_node *parent_node;
  123. struct irq_fwspec fwspec;
  124. int p_hwirq, err = 0;
  125. u16 out_irq;
  126. out_irq = ti_sci_get_free_resource(intr->out_irqs);
  127. if (out_irq == TI_SCI_RESOURCE_NULL)
  128. return -EINVAL;
  129. p_hwirq = ti_sci_intr_xlate_irq(intr, out_irq);
  130. if (p_hwirq < 0)
  131. goto err_irqs;
  132. parent_node = of_irq_find_parent(dev_of_node(intr->dev));
  133. fwspec.fwnode = of_node_to_fwnode(parent_node);
  134. if (of_device_is_compatible(parent_node, "arm,gic-v3")) {
  135. /* Parent is GIC */
  136. fwspec.param_count = 3;
  137. fwspec.param[0] = 0; /* SPI */
  138. fwspec.param[1] = p_hwirq - 32; /* SPI offset */
  139. fwspec.param[2] = intr->type;
  140. } else {
  141. /* Parent is Interrupt Router */
  142. fwspec.param_count = 1;
  143. fwspec.param[0] = p_hwirq;
  144. }
  145. err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
  146. if (err)
  147. goto err_irqs;
  148. err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci,
  149. intr->ti_sci_id, hwirq,
  150. intr->ti_sci_id, out_irq);
  151. if (err)
  152. goto err_msg;
  153. return out_irq;
  154. err_msg:
  155. irq_domain_free_irqs_parent(domain, virq, 1);
  156. err_irqs:
  157. ti_sci_release_resource(intr->out_irqs, out_irq);
  158. return err;
  159. }
  160. /**
  161. * ti_sci_intr_irq_domain_alloc() - Allocate Interrupt router IRQs
  162. * @domain: Point to the interrupt router IRQ domain
  163. * @virq: Corresponding Linux virtual IRQ number
  164. * @nr_irqs: Continuous irqs to be allocated
  165. * @data: Pointer to firmware specifier
  166. *
  167. * Return 0 if all went well else appropriate error value.
  168. */
  169. static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain,
  170. unsigned int virq, unsigned int nr_irqs,
  171. void *data)
  172. {
  173. struct irq_fwspec *fwspec = data;
  174. unsigned long hwirq;
  175. unsigned int flags;
  176. int err, out_irq;
  177. err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags);
  178. if (err)
  179. return err;
  180. out_irq = ti_sci_intr_alloc_parent_irq(domain, virq, hwirq);
  181. if (out_irq < 0)
  182. return out_irq;
  183. irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
  184. &ti_sci_intr_irq_chip,
  185. (void *)(uintptr_t)out_irq);
  186. return 0;
  187. }
  188. static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = {
  189. .free = ti_sci_intr_irq_domain_free,
  190. .alloc = ti_sci_intr_irq_domain_alloc,
  191. .translate = ti_sci_intr_irq_domain_translate,
  192. };
  193. static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
  194. {
  195. struct irq_domain *parent_domain, *domain;
  196. struct ti_sci_intr_irq_domain *intr;
  197. struct device_node *parent_node;
  198. struct device *dev = &pdev->dev;
  199. int ret;
  200. parent_node = of_irq_find_parent(dev_of_node(dev));
  201. if (!parent_node) {
  202. dev_err(dev, "Failed to get IRQ parent node\n");
  203. return -ENODEV;
  204. }
  205. parent_domain = irq_find_host(parent_node);
  206. if (!parent_domain) {
  207. dev_err(dev, "Failed to find IRQ parent domain\n");
  208. return -ENODEV;
  209. }
  210. intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL);
  211. if (!intr)
  212. return -ENOMEM;
  213. intr->dev = dev;
  214. ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type",
  215. &intr->type);
  216. if (ret) {
  217. dev_err(dev, "missing ti,intr-trigger-type property\n");
  218. return -EINVAL;
  219. }
  220. intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
  221. if (IS_ERR(intr->sci))
  222. return dev_err_probe(dev, PTR_ERR(intr->sci),
  223. "ti,sci read fail\n");
  224. ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dev-id",
  225. &intr->ti_sci_id);
  226. if (ret) {
  227. dev_err(dev, "missing 'ti,sci-dev-id' property\n");
  228. return -EINVAL;
  229. }
  230. intr->out_irqs = devm_ti_sci_get_resource(intr->sci, dev,
  231. intr->ti_sci_id,
  232. TI_SCI_RESASG_SUBTYPE_IR_OUTPUT);
  233. if (IS_ERR(intr->out_irqs)) {
  234. dev_err(dev, "Destination irq resource allocation failed\n");
  235. return PTR_ERR(intr->out_irqs);
  236. }
  237. domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev),
  238. &ti_sci_intr_irq_domain_ops, intr);
  239. if (!domain) {
  240. dev_err(dev, "Failed to allocate IRQ domain\n");
  241. return -ENOMEM;
  242. }
  243. dev_info(dev, "Interrupt Router %d domain created\n", intr->ti_sci_id);
  244. return 0;
  245. }
  246. static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = {
  247. { .compatible = "ti,sci-intr", },
  248. { /* sentinel */ },
  249. };
  250. MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match);
  251. static struct platform_driver ti_sci_intr_irq_domain_driver = {
  252. .probe = ti_sci_intr_irq_domain_probe,
  253. .driver = {
  254. .name = "ti-sci-intr",
  255. .of_match_table = ti_sci_intr_irq_domain_of_match,
  256. },
  257. };
  258. module_platform_driver(ti_sci_intr_irq_domain_driver);
  259. MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>");
  260. MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol");
  261. MODULE_LICENSE("GPL v2");