irq-loongson-htvec.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
  4. * Loongson HyperTransport Interrupt Vector support
  5. */
  6. #define pr_fmt(fmt) "htvec: " fmt
  7. #include <linux/interrupt.h>
  8. #include <linux/irq.h>
  9. #include <linux/irqchip.h>
  10. #include <linux/irqdomain.h>
  11. #include <linux/irqchip/chained_irq.h>
  12. #include <linux/kernel.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/of_address.h>
  15. #include <linux/of_irq.h>
  16. #include <linux/of_platform.h>
  17. /* Registers */
  18. #define HTVEC_EN_OFF 0x20
  19. #define HTVEC_MAX_PARENT_IRQ 8
  20. #define VEC_COUNT_PER_REG 32
  21. #define VEC_REG_IDX(irq_id) ((irq_id) / VEC_COUNT_PER_REG)
  22. #define VEC_REG_BIT(irq_id) ((irq_id) % VEC_COUNT_PER_REG)
  23. struct htvec {
  24. int num_parents;
  25. void __iomem *base;
  26. struct irq_domain *htvec_domain;
  27. raw_spinlock_t htvec_lock;
  28. };
  29. static void htvec_irq_dispatch(struct irq_desc *desc)
  30. {
  31. int i;
  32. u32 pending;
  33. bool handled = false;
  34. struct irq_chip *chip = irq_desc_get_chip(desc);
  35. struct htvec *priv = irq_desc_get_handler_data(desc);
  36. chained_irq_enter(chip, desc);
  37. for (i = 0; i < priv->num_parents; i++) {
  38. pending = readl(priv->base + 4 * i);
  39. while (pending) {
  40. int bit = __ffs(pending);
  41. generic_handle_irq(irq_linear_revmap(priv->htvec_domain, bit +
  42. VEC_COUNT_PER_REG * i));
  43. pending &= ~BIT(bit);
  44. handled = true;
  45. }
  46. }
  47. if (!handled)
  48. spurious_interrupt();
  49. chained_irq_exit(chip, desc);
  50. }
  51. static void htvec_ack_irq(struct irq_data *d)
  52. {
  53. struct htvec *priv = irq_data_get_irq_chip_data(d);
  54. writel(BIT(VEC_REG_BIT(d->hwirq)),
  55. priv->base + VEC_REG_IDX(d->hwirq) * 4);
  56. }
  57. static void htvec_mask_irq(struct irq_data *d)
  58. {
  59. u32 reg;
  60. void __iomem *addr;
  61. struct htvec *priv = irq_data_get_irq_chip_data(d);
  62. raw_spin_lock(&priv->htvec_lock);
  63. addr = priv->base + HTVEC_EN_OFF;
  64. addr += VEC_REG_IDX(d->hwirq) * 4;
  65. reg = readl(addr);
  66. reg &= ~BIT(VEC_REG_BIT(d->hwirq));
  67. writel(reg, addr);
  68. raw_spin_unlock(&priv->htvec_lock);
  69. }
  70. static void htvec_unmask_irq(struct irq_data *d)
  71. {
  72. u32 reg;
  73. void __iomem *addr;
  74. struct htvec *priv = irq_data_get_irq_chip_data(d);
  75. raw_spin_lock(&priv->htvec_lock);
  76. addr = priv->base + HTVEC_EN_OFF;
  77. addr += VEC_REG_IDX(d->hwirq) * 4;
  78. reg = readl(addr);
  79. reg |= BIT(VEC_REG_BIT(d->hwirq));
  80. writel(reg, addr);
  81. raw_spin_unlock(&priv->htvec_lock);
  82. }
  83. static struct irq_chip htvec_irq_chip = {
  84. .name = "LOONGSON_HTVEC",
  85. .irq_mask = htvec_mask_irq,
  86. .irq_unmask = htvec_unmask_irq,
  87. .irq_ack = htvec_ack_irq,
  88. };
  89. static int htvec_domain_alloc(struct irq_domain *domain, unsigned int virq,
  90. unsigned int nr_irqs, void *arg)
  91. {
  92. int ret;
  93. unsigned long hwirq;
  94. unsigned int type, i;
  95. struct htvec *priv = domain->host_data;
  96. ret = irq_domain_translate_onecell(domain, arg, &hwirq, &type);
  97. if (ret)
  98. return ret;
  99. for (i = 0; i < nr_irqs; i++) {
  100. irq_domain_set_info(domain, virq + i, hwirq + i, &htvec_irq_chip,
  101. priv, handle_edge_irq, NULL, NULL);
  102. }
  103. return 0;
  104. }
  105. static void htvec_domain_free(struct irq_domain *domain, unsigned int virq,
  106. unsigned int nr_irqs)
  107. {
  108. int i;
  109. for (i = 0; i < nr_irqs; i++) {
  110. struct irq_data *d = irq_domain_get_irq_data(domain, virq + i);
  111. irq_set_handler(virq + i, NULL);
  112. irq_domain_reset_irq_data(d);
  113. }
  114. }
  115. static const struct irq_domain_ops htvec_domain_ops = {
  116. .translate = irq_domain_translate_onecell,
  117. .alloc = htvec_domain_alloc,
  118. .free = htvec_domain_free,
  119. };
  120. static void htvec_reset(struct htvec *priv)
  121. {
  122. u32 idx;
  123. /* Clear IRQ cause registers, mask all interrupts */
  124. for (idx = 0; idx < priv->num_parents; idx++) {
  125. writel_relaxed(0x0, priv->base + HTVEC_EN_OFF + 4 * idx);
  126. writel_relaxed(0xFFFFFFFF, priv->base + 4 * idx);
  127. }
  128. }
  129. static int htvec_of_init(struct device_node *node,
  130. struct device_node *parent)
  131. {
  132. struct htvec *priv;
  133. int err, parent_irq[8], i;
  134. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  135. if (!priv)
  136. return -ENOMEM;
  137. raw_spin_lock_init(&priv->htvec_lock);
  138. priv->base = of_iomap(node, 0);
  139. if (!priv->base) {
  140. err = -ENOMEM;
  141. goto free_priv;
  142. }
  143. /* Interrupt may come from any of the 8 interrupt lines */
  144. for (i = 0; i < HTVEC_MAX_PARENT_IRQ; i++) {
  145. parent_irq[i] = irq_of_parse_and_map(node, i);
  146. if (parent_irq[i] <= 0)
  147. break;
  148. priv->num_parents++;
  149. }
  150. if (!priv->num_parents) {
  151. pr_err("Failed to get parent irqs\n");
  152. err = -ENODEV;
  153. goto iounmap_base;
  154. }
  155. priv->htvec_domain = irq_domain_create_linear(of_node_to_fwnode(node),
  156. (VEC_COUNT_PER_REG * priv->num_parents),
  157. &htvec_domain_ops, priv);
  158. if (!priv->htvec_domain) {
  159. pr_err("Failed to create IRQ domain\n");
  160. err = -ENOMEM;
  161. goto irq_dispose;
  162. }
  163. htvec_reset(priv);
  164. for (i = 0; i < priv->num_parents; i++)
  165. irq_set_chained_handler_and_data(parent_irq[i],
  166. htvec_irq_dispatch, priv);
  167. return 0;
  168. irq_dispose:
  169. for (; i > 0; i--)
  170. irq_dispose_mapping(parent_irq[i - 1]);
  171. iounmap_base:
  172. iounmap(priv->base);
  173. free_priv:
  174. kfree(priv);
  175. return err;
  176. }
  177. IRQCHIP_DECLARE(htvec, "loongson,htvec-1.0", htvec_of_init);