irq-loongson-htpic.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
  4. * Loongson HTPIC IRQ support
  5. */
  6. #include <linux/init.h>
  7. #include <linux/of_address.h>
  8. #include <linux/of_irq.h>
  9. #include <linux/irqchip.h>
  10. #include <linux/irqchip/chained_irq.h>
  11. #include <linux/irq.h>
  12. #include <linux/io.h>
  13. #include <linux/syscore_ops.h>
  14. #include <asm/i8259.h>
  15. #define HTPIC_MAX_PARENT_IRQ 4
  16. #define HTINT_NUM_VECTORS 8
  17. #define HTINT_EN_OFF 0x20
  18. struct loongson_htpic {
  19. void __iomem *base;
  20. struct irq_domain *domain;
  21. };
  22. static struct loongson_htpic *htpic;
  23. static void htpic_irq_dispatch(struct irq_desc *desc)
  24. {
  25. struct loongson_htpic *priv = irq_desc_get_handler_data(desc);
  26. struct irq_chip *chip = irq_desc_get_chip(desc);
  27. uint32_t pending;
  28. chained_irq_enter(chip, desc);
  29. pending = readl(priv->base);
  30. /* Ack all IRQs at once, otherwise IRQ flood might happen */
  31. writel(pending, priv->base);
  32. if (!pending)
  33. spurious_interrupt();
  34. while (pending) {
  35. int bit = __ffs(pending);
  36. if (unlikely(bit > 15)) {
  37. spurious_interrupt();
  38. break;
  39. }
  40. generic_handle_irq(irq_linear_revmap(priv->domain, bit));
  41. pending &= ~BIT(bit);
  42. }
  43. chained_irq_exit(chip, desc);
  44. }
  45. static void htpic_reg_init(void)
  46. {
  47. int i;
  48. for (i = 0; i < HTINT_NUM_VECTORS; i++) {
  49. uint32_t val;
  50. /* Disable all HT Vectors */
  51. writel(0x0, htpic->base + HTINT_EN_OFF + i * 0x4);
  52. val = readl(htpic->base + i * 0x4);
  53. /* Ack all possible pending IRQs */
  54. writel(GENMASK(31, 0), htpic->base + i * 0x4);
  55. }
  56. /* Enable 16 vectors for PIC */
  57. writel(0xffff, htpic->base + HTINT_EN_OFF);
  58. }
  59. static void htpic_resume(void)
  60. {
  61. htpic_reg_init();
  62. }
  63. struct syscore_ops htpic_syscore_ops = {
  64. .resume = htpic_resume,
  65. };
  66. int __init htpic_of_init(struct device_node *node, struct device_node *parent)
  67. {
  68. unsigned int parent_irq[4];
  69. int i, err;
  70. int num_parents = 0;
  71. if (htpic) {
  72. pr_err("loongson-htpic: Only one HTPIC is allowed in the system\n");
  73. return -ENODEV;
  74. }
  75. htpic = kzalloc(sizeof(*htpic), GFP_KERNEL);
  76. if (!htpic)
  77. return -ENOMEM;
  78. htpic->base = of_iomap(node, 0);
  79. if (!htpic->base) {
  80. err = -ENODEV;
  81. goto out_free;
  82. }
  83. htpic->domain = __init_i8259_irqs(node);
  84. if (!htpic->domain) {
  85. pr_err("loongson-htpic: Failed to initialize i8259 IRQs\n");
  86. err = -ENOMEM;
  87. goto out_iounmap;
  88. }
  89. /* Interrupt may come from any of the 4 interrupt line */
  90. for (i = 0; i < HTPIC_MAX_PARENT_IRQ; i++) {
  91. parent_irq[i] = irq_of_parse_and_map(node, i);
  92. if (parent_irq[i] <= 0)
  93. break;
  94. num_parents++;
  95. }
  96. if (!num_parents) {
  97. pr_err("loongson-htpic: Failed to get parent irqs\n");
  98. err = -ENODEV;
  99. goto out_remove_domain;
  100. }
  101. htpic_reg_init();
  102. for (i = 0; i < num_parents; i++) {
  103. irq_set_chained_handler_and_data(parent_irq[i],
  104. htpic_irq_dispatch, htpic);
  105. }
  106. register_syscore_ops(&htpic_syscore_ops);
  107. return 0;
  108. out_remove_domain:
  109. irq_domain_remove(htpic->domain);
  110. out_iounmap:
  111. iounmap(htpic->base);
  112. out_free:
  113. kfree(htpic);
  114. return err;
  115. }
  116. IRQCHIP_DECLARE(loongson_htpic, "loongson,htpic-1.0", htpic_of_init);