irq-ti-sci-intr.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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.h>
  15. #include <linux/of_irq.h>
  16. #include <linux/platform_device.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 (intr->type) {
  60. /* Global interrupt-type */
  61. if (fwspec->param_count != 1)
  62. return -EINVAL;
  63. *hwirq = fwspec->param[0];
  64. *type = intr->type;
  65. } else {
  66. /* Per-Line interrupt-type */
  67. if (fwspec->param_count != 2)
  68. return -EINVAL;
  69. *hwirq = fwspec->param[0];
  70. *type = fwspec->param[1];
  71. }
  72. return 0;
  73. }
  74. /**
  75. * ti_sci_intr_xlate_irq() - Translate hwirq to parent's hwirq.
  76. * @intr: IRQ domain corresponding to Interrupt Router
  77. * @irq: Hardware irq corresponding to the above irq domain
  78. *
  79. * Return parent irq number if translation is available else -ENOENT.
  80. */
  81. static int ti_sci_intr_xlate_irq(struct ti_sci_intr_irq_domain *intr, u32 irq)
  82. {
  83. struct device_node *np = dev_of_node(intr->dev);
  84. u32 base, pbase, size, len;
  85. const __be32 *range;
  86. range = of_get_property(np, "ti,interrupt-ranges", &len);
  87. if (!range)
  88. return irq;
  89. for (len /= sizeof(*range); len >= 3; len -= 3) {
  90. base = be32_to_cpu(*range++);
  91. pbase = be32_to_cpu(*range++);
  92. size = be32_to_cpu(*range++);
  93. if (base <= irq && irq < base + size)
  94. return irq - base + pbase;
  95. }
  96. return -ENOENT;
  97. }
  98. /**
  99. * ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain.
  100. * @domain: Domain to which the irqs belong
  101. * @virq: Linux virtual IRQ to be freed.
  102. * @nr_irqs: Number of continuous irqs to be freed
  103. */
  104. static void ti_sci_intr_irq_domain_free(struct irq_domain *domain,
  105. unsigned int virq, unsigned int nr_irqs)
  106. {
  107. struct ti_sci_intr_irq_domain *intr = domain->host_data;
  108. struct irq_data *data;
  109. int out_irq;
  110. data = irq_domain_get_irq_data(domain, virq);
  111. out_irq = (uintptr_t)data->chip_data;
  112. intr->sci->ops.rm_irq_ops.free_irq(intr->sci,
  113. intr->ti_sci_id, data->hwirq,
  114. intr->ti_sci_id, out_irq);
  115. ti_sci_release_resource(intr->out_irqs, out_irq);
  116. irq_domain_free_irqs_parent(domain, virq, 1);
  117. irq_domain_reset_irq_data(data);
  118. }
  119. /**
  120. * ti_sci_intr_alloc_parent_irq() - Allocate parent IRQ
  121. * @domain: Pointer to the interrupt router IRQ domain
  122. * @virq: Corresponding Linux virtual IRQ number
  123. * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain
  124. * @hwirq_type: Corresponding hwirq trigger type for the IRQ within this IRQ domain
  125. *
  126. * Returns intr output irq if all went well else appropriate error pointer.
  127. */
  128. static int ti_sci_intr_alloc_parent_irq(struct irq_domain *domain, unsigned int virq,
  129. u32 hwirq, u32 hwirq_type)
  130. {
  131. struct ti_sci_intr_irq_domain *intr = domain->host_data;
  132. struct device_node *parent_node;
  133. struct irq_fwspec fwspec;
  134. int p_hwirq, err = 0;
  135. u16 out_irq;
  136. out_irq = ti_sci_get_free_resource(intr->out_irqs);
  137. if (out_irq == TI_SCI_RESOURCE_NULL)
  138. return -EINVAL;
  139. p_hwirq = ti_sci_intr_xlate_irq(intr, out_irq);
  140. if (p_hwirq < 0)
  141. goto err_irqs;
  142. parent_node = of_irq_find_parent(dev_of_node(intr->dev));
  143. fwspec.fwnode = of_fwnode_handle(parent_node);
  144. if (of_device_is_compatible(parent_node, "arm,gic-v3")) {
  145. /* Parent is GIC */
  146. fwspec.param_count = 3;
  147. fwspec.param[0] = 0; /* SPI */
  148. fwspec.param[1] = p_hwirq - 32; /* SPI offset */
  149. fwspec.param[2] = hwirq_type;
  150. } else {
  151. /* Parent is Interrupt Router */
  152. u32 parent_trigger_type;
  153. if (!of_property_read_u32(parent_node, "ti,intr-trigger-type",
  154. &parent_trigger_type)) {
  155. /* Parent has global trigger type */
  156. fwspec.param_count = 1;
  157. fwspec.param[0] = p_hwirq;
  158. } else {
  159. /* Parent supports per-line trigger types */
  160. fwspec.param_count = 2;
  161. fwspec.param[0] = p_hwirq;
  162. fwspec.param[1] = hwirq_type;
  163. }
  164. }
  165. err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
  166. if (err)
  167. goto err_irqs;
  168. err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci,
  169. intr->ti_sci_id, hwirq,
  170. intr->ti_sci_id, out_irq);
  171. if (err)
  172. goto err_msg;
  173. return out_irq;
  174. err_msg:
  175. irq_domain_free_irqs_parent(domain, virq, 1);
  176. err_irqs:
  177. ti_sci_release_resource(intr->out_irqs, out_irq);
  178. return err;
  179. }
  180. /**
  181. * ti_sci_intr_irq_domain_alloc() - Allocate Interrupt router IRQs
  182. * @domain: Point to the interrupt router IRQ domain
  183. * @virq: Corresponding Linux virtual IRQ number
  184. * @nr_irqs: Continuous irqs to be allocated
  185. * @data: Pointer to firmware specifier
  186. *
  187. * Return 0 if all went well else appropriate error value.
  188. */
  189. static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain,
  190. unsigned int virq, unsigned int nr_irqs,
  191. void *data)
  192. {
  193. struct irq_fwspec *fwspec = data;
  194. unsigned int hwirq_type;
  195. unsigned long hwirq;
  196. int err, out_irq;
  197. err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &hwirq_type);
  198. if (err)
  199. return err;
  200. out_irq = ti_sci_intr_alloc_parent_irq(domain, virq, hwirq, hwirq_type);
  201. if (out_irq < 0)
  202. return out_irq;
  203. irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
  204. &ti_sci_intr_irq_chip,
  205. (void *)(uintptr_t)out_irq);
  206. return 0;
  207. }
  208. static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = {
  209. .free = ti_sci_intr_irq_domain_free,
  210. .alloc = ti_sci_intr_irq_domain_alloc,
  211. .translate = ti_sci_intr_irq_domain_translate,
  212. };
  213. static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev)
  214. {
  215. struct irq_domain *parent_domain, *domain;
  216. struct ti_sci_intr_irq_domain *intr;
  217. struct device_node *parent_node;
  218. struct device *dev = &pdev->dev;
  219. int ret;
  220. parent_node = of_irq_find_parent(dev_of_node(dev));
  221. if (!parent_node) {
  222. dev_err(dev, "Failed to get IRQ parent node\n");
  223. return -ENODEV;
  224. }
  225. parent_domain = irq_find_host(parent_node);
  226. of_node_put(parent_node);
  227. if (!parent_domain) {
  228. dev_err(dev, "Failed to find IRQ parent domain\n");
  229. return -ENODEV;
  230. }
  231. intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL);
  232. if (!intr)
  233. return -ENOMEM;
  234. intr->dev = dev;
  235. if (of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", &intr->type))
  236. intr->type = IRQ_TYPE_NONE;
  237. intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci");
  238. if (IS_ERR(intr->sci))
  239. return dev_err_probe(dev, PTR_ERR(intr->sci),
  240. "ti,sci read fail\n");
  241. ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dev-id",
  242. &intr->ti_sci_id);
  243. if (ret) {
  244. dev_err(dev, "missing 'ti,sci-dev-id' property\n");
  245. return -EINVAL;
  246. }
  247. intr->out_irqs = devm_ti_sci_get_resource(intr->sci, dev,
  248. intr->ti_sci_id,
  249. TI_SCI_RESASG_SUBTYPE_IR_OUTPUT);
  250. if (IS_ERR(intr->out_irqs)) {
  251. dev_err(dev, "Destination irq resource allocation failed\n");
  252. return PTR_ERR(intr->out_irqs);
  253. }
  254. domain = irq_domain_create_hierarchy(parent_domain, 0, 0, dev_fwnode(dev),
  255. &ti_sci_intr_irq_domain_ops, intr);
  256. if (!domain) {
  257. dev_err(dev, "Failed to allocate IRQ domain\n");
  258. return -ENOMEM;
  259. }
  260. dev_info(dev, "Interrupt Router %d domain created\n", intr->ti_sci_id);
  261. return 0;
  262. }
  263. static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = {
  264. { .compatible = "ti,sci-intr", },
  265. { /* sentinel */ },
  266. };
  267. MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match);
  268. static struct platform_driver ti_sci_intr_irq_domain_driver = {
  269. .probe = ti_sci_intr_irq_domain_probe,
  270. .driver = {
  271. .name = "ti-sci-intr",
  272. .of_match_table = ti_sci_intr_irq_domain_of_match,
  273. },
  274. };
  275. module_platform_driver(ti_sci_intr_irq_domain_driver);
  276. MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>");
  277. MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol");
  278. MODULE_LICENSE("GPL");