irq-riscv-imsic-early.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2021 Western Digital Corporation or its affiliates.
  4. * Copyright (C) 2022 Ventana Micro Systems Inc.
  5. */
  6. #define pr_fmt(fmt) "riscv-imsic: " fmt
  7. #include <linux/acpi.h>
  8. #include <linux/cpu.h>
  9. #include <linux/interrupt.h>
  10. #include <linux/io.h>
  11. #include <linux/irq.h>
  12. #include <linux/irqchip.h>
  13. #include <linux/irqchip/chained_irq.h>
  14. #include <linux/irqchip/riscv-imsic.h>
  15. #include <linux/module.h>
  16. #include <linux/pci.h>
  17. #include <linux/spinlock.h>
  18. #include <linux/smp.h>
  19. #include "irq-riscv-imsic-state.h"
  20. static int imsic_parent_irq;
  21. #ifdef CONFIG_SMP
  22. static void imsic_ipi_send(unsigned int cpu)
  23. {
  24. struct imsic_local_config *local = per_cpu_ptr(imsic->global.local, cpu);
  25. writel_relaxed(IMSIC_IPI_ID, local->msi_va);
  26. }
  27. static void imsic_ipi_starting_cpu(void)
  28. {
  29. /* Enable IPIs for current CPU. */
  30. __imsic_id_set_enable(IMSIC_IPI_ID);
  31. }
  32. static void imsic_ipi_dying_cpu(void)
  33. {
  34. /* Disable IPIs for current CPU. */
  35. __imsic_id_clear_enable(IMSIC_IPI_ID);
  36. }
  37. static int __init imsic_ipi_domain_init(void)
  38. {
  39. int virq;
  40. /* Create IMSIC IPI multiplexing */
  41. virq = ipi_mux_create(IMSIC_NR_IPI, imsic_ipi_send);
  42. if (virq <= 0)
  43. return virq < 0 ? virq : -ENOMEM;
  44. /* Set vIRQ range */
  45. riscv_ipi_set_virq_range(virq, IMSIC_NR_IPI);
  46. /* Announce that IMSIC is providing IPIs */
  47. pr_info("%pfwP: providing IPIs using interrupt %d\n", imsic->fwnode, IMSIC_IPI_ID);
  48. return 0;
  49. }
  50. #else
  51. static void imsic_ipi_starting_cpu(void) { }
  52. static void imsic_ipi_dying_cpu(void) { }
  53. static int __init imsic_ipi_domain_init(void) { return 0; }
  54. #endif
  55. /*
  56. * To handle an interrupt, we read the TOPEI CSR and write zero in one
  57. * instruction. If TOPEI CSR is non-zero then we translate TOPEI.ID to
  58. * Linux interrupt number and let Linux IRQ subsystem handle it.
  59. */
  60. static void imsic_handle_irq(struct irq_desc *desc)
  61. {
  62. struct irq_chip *chip = irq_desc_get_chip(desc);
  63. int err, cpu = smp_processor_id();
  64. struct imsic_vector *vec;
  65. unsigned long local_id;
  66. /*
  67. * Process pending local synchronization instead of waiting
  68. * for per-CPU local timer to expire.
  69. */
  70. imsic_local_sync_all(false);
  71. chained_irq_enter(chip, desc);
  72. while ((local_id = csr_swap(CSR_TOPEI, 0))) {
  73. local_id >>= TOPEI_ID_SHIFT;
  74. if (local_id == IMSIC_IPI_ID) {
  75. if (IS_ENABLED(CONFIG_SMP))
  76. ipi_mux_process();
  77. continue;
  78. }
  79. if (unlikely(!imsic->base_domain))
  80. continue;
  81. vec = imsic_vector_from_local_id(cpu, local_id);
  82. if (!vec) {
  83. pr_warn_ratelimited("vector not found for local ID 0x%lx\n", local_id);
  84. continue;
  85. }
  86. err = generic_handle_domain_irq(imsic->base_domain, vec->hwirq);
  87. if (unlikely(err))
  88. pr_warn_ratelimited("hwirq 0x%x mapping not found\n", vec->hwirq);
  89. }
  90. chained_irq_exit(chip, desc);
  91. }
  92. static int imsic_starting_cpu(unsigned int cpu)
  93. {
  94. /* Mark per-CPU IMSIC state as online */
  95. imsic_state_online();
  96. /* Enable per-CPU parent interrupt */
  97. enable_percpu_irq(imsic_parent_irq, irq_get_trigger_type(imsic_parent_irq));
  98. /* Setup IPIs */
  99. imsic_ipi_starting_cpu();
  100. /*
  101. * Interrupts identities might have been enabled/disabled while
  102. * this CPU was not running so sync-up local enable/disable state.
  103. */
  104. imsic_local_sync_all(true);
  105. /* Enable local interrupt delivery */
  106. imsic_local_delivery(true);
  107. return 0;
  108. }
  109. static int imsic_dying_cpu(unsigned int cpu)
  110. {
  111. /* Cleanup IPIs */
  112. imsic_ipi_dying_cpu();
  113. /* Mark per-CPU IMSIC state as offline */
  114. imsic_state_offline();
  115. return 0;
  116. }
  117. static int __init imsic_early_probe(struct fwnode_handle *fwnode)
  118. {
  119. struct irq_domain *domain;
  120. int rc;
  121. /* Find parent domain and register chained handler */
  122. domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY);
  123. if (!domain) {
  124. pr_err("%pfwP: Failed to find INTC domain\n", fwnode);
  125. return -ENOENT;
  126. }
  127. imsic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
  128. if (!imsic_parent_irq) {
  129. pr_err("%pfwP: Failed to create INTC mapping\n", fwnode);
  130. return -ENOENT;
  131. }
  132. /* Initialize IPI domain */
  133. rc = imsic_ipi_domain_init();
  134. if (rc) {
  135. pr_err("%pfwP: Failed to initialize IPI domain\n", fwnode);
  136. return rc;
  137. }
  138. /* Setup chained handler to the parent domain interrupt */
  139. irq_set_chained_handler(imsic_parent_irq, imsic_handle_irq);
  140. /*
  141. * Setup cpuhp state (must be done after setting imsic_parent_irq)
  142. *
  143. * Don't disable per-CPU IMSIC file when CPU goes offline
  144. * because this affects IPI and the masking/unmasking of
  145. * virtual IPIs is done via generic IPI-Mux
  146. */
  147. cpuhp_setup_state(CPUHP_AP_IRQ_RISCV_IMSIC_STARTING, "irqchip/riscv/imsic:starting",
  148. imsic_starting_cpu, imsic_dying_cpu);
  149. return 0;
  150. }
  151. static int __init imsic_early_dt_init(struct device_node *node, struct device_node *parent)
  152. {
  153. struct fwnode_handle *fwnode = &node->fwnode;
  154. int rc;
  155. /* Setup IMSIC state */
  156. rc = imsic_setup_state(fwnode, NULL);
  157. if (rc) {
  158. pr_err("%pfwP: failed to setup state (error %d)\n", fwnode, rc);
  159. return rc;
  160. }
  161. /* Do early setup of IPIs */
  162. rc = imsic_early_probe(fwnode);
  163. if (rc)
  164. return rc;
  165. /* Ensure that OF platform device gets probed */
  166. of_node_clear_flag(node, OF_POPULATED);
  167. return 0;
  168. }
  169. IRQCHIP_DECLARE(riscv_imsic, "riscv,imsics", imsic_early_dt_init);
  170. #ifdef CONFIG_ACPI
  171. static struct fwnode_handle *imsic_acpi_fwnode;
  172. struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev)
  173. {
  174. return imsic_acpi_fwnode;
  175. }
  176. static int __init imsic_early_acpi_init(union acpi_subtable_headers *header,
  177. const unsigned long end)
  178. {
  179. struct acpi_madt_imsic *imsic = (struct acpi_madt_imsic *)header;
  180. int rc;
  181. imsic_acpi_fwnode = irq_domain_alloc_named_fwnode("imsic");
  182. if (!imsic_acpi_fwnode) {
  183. pr_err("unable to allocate IMSIC FW node\n");
  184. return -ENOMEM;
  185. }
  186. /* Setup IMSIC state */
  187. rc = imsic_setup_state(imsic_acpi_fwnode, imsic);
  188. if (rc) {
  189. pr_err("%pfwP: failed to setup state (error %d)\n", imsic_acpi_fwnode, rc);
  190. return rc;
  191. }
  192. /* Do early setup of IMSIC state and IPIs */
  193. rc = imsic_early_probe(imsic_acpi_fwnode);
  194. if (rc) {
  195. irq_domain_free_fwnode(imsic_acpi_fwnode);
  196. imsic_acpi_fwnode = NULL;
  197. return rc;
  198. }
  199. rc = imsic_platform_acpi_probe(imsic_acpi_fwnode);
  200. #ifdef CONFIG_PCI
  201. if (!rc)
  202. pci_msi_register_fwnode_provider(&imsic_acpi_get_fwnode);
  203. #endif
  204. if (rc)
  205. pr_err("%pfwP: failed to register IMSIC for MSI functionality (error %d)\n",
  206. imsic_acpi_fwnode, rc);
  207. /*
  208. * Even if imsic_platform_acpi_probe() fails, the IPI part of IMSIC can
  209. * continue to work. So, no need to return failure. This is similar to
  210. * DT where IPI works but MSI probe fails for some reason.
  211. */
  212. return 0;
  213. }
  214. IRQCHIP_ACPI_DECLARE(riscv_imsic, ACPI_MADT_TYPE_IMSIC, NULL,
  215. 1, imsic_early_acpi_init);
  216. #endif