irq-mst-intc.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
  2. /*
  3. * Copyright (c) 2020 MediaTek Inc.
  4. * Author Mark-PK Tsai <mark-pk.tsai@mediatek.com>
  5. */
  6. #include <linux/interrupt.h>
  7. #include <linux/io.h>
  8. #include <linux/irq.h>
  9. #include <linux/irqchip.h>
  10. #include <linux/irqdomain.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_irq.h>
  14. #include <linux/slab.h>
  15. #include <linux/spinlock.h>
  16. #define INTC_MASK 0x0
  17. #define INTC_EOI 0x20
  18. struct mst_intc_chip_data {
  19. raw_spinlock_t lock;
  20. unsigned int irq_start, nr_irqs;
  21. void __iomem *base;
  22. bool no_eoi;
  23. };
  24. static void mst_set_irq(struct irq_data *d, u32 offset)
  25. {
  26. irq_hw_number_t hwirq = irqd_to_hwirq(d);
  27. struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d);
  28. u16 val, mask;
  29. unsigned long flags;
  30. mask = 1 << (hwirq % 16);
  31. offset += (hwirq / 16) * 4;
  32. raw_spin_lock_irqsave(&cd->lock, flags);
  33. val = readw_relaxed(cd->base + offset) | mask;
  34. writew_relaxed(val, cd->base + offset);
  35. raw_spin_unlock_irqrestore(&cd->lock, flags);
  36. }
  37. static void mst_clear_irq(struct irq_data *d, u32 offset)
  38. {
  39. irq_hw_number_t hwirq = irqd_to_hwirq(d);
  40. struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d);
  41. u16 val, mask;
  42. unsigned long flags;
  43. mask = 1 << (hwirq % 16);
  44. offset += (hwirq / 16) * 4;
  45. raw_spin_lock_irqsave(&cd->lock, flags);
  46. val = readw_relaxed(cd->base + offset) & ~mask;
  47. writew_relaxed(val, cd->base + offset);
  48. raw_spin_unlock_irqrestore(&cd->lock, flags);
  49. }
  50. static void mst_intc_mask_irq(struct irq_data *d)
  51. {
  52. mst_set_irq(d, INTC_MASK);
  53. irq_chip_mask_parent(d);
  54. }
  55. static void mst_intc_unmask_irq(struct irq_data *d)
  56. {
  57. mst_clear_irq(d, INTC_MASK);
  58. irq_chip_unmask_parent(d);
  59. }
  60. static void mst_intc_eoi_irq(struct irq_data *d)
  61. {
  62. struct mst_intc_chip_data *cd = irq_data_get_irq_chip_data(d);
  63. if (!cd->no_eoi)
  64. mst_set_irq(d, INTC_EOI);
  65. irq_chip_eoi_parent(d);
  66. }
  67. static struct irq_chip mst_intc_chip = {
  68. .name = "mst-intc",
  69. .irq_mask = mst_intc_mask_irq,
  70. .irq_unmask = mst_intc_unmask_irq,
  71. .irq_eoi = mst_intc_eoi_irq,
  72. .irq_get_irqchip_state = irq_chip_get_parent_state,
  73. .irq_set_irqchip_state = irq_chip_set_parent_state,
  74. .irq_set_affinity = irq_chip_set_affinity_parent,
  75. .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent,
  76. .irq_set_type = irq_chip_set_type_parent,
  77. .irq_retrigger = irq_chip_retrigger_hierarchy,
  78. .flags = IRQCHIP_SET_TYPE_MASKED |
  79. IRQCHIP_SKIP_SET_WAKE |
  80. IRQCHIP_MASK_ON_SUSPEND,
  81. };
  82. static int mst_intc_domain_translate(struct irq_domain *d,
  83. struct irq_fwspec *fwspec,
  84. unsigned long *hwirq,
  85. unsigned int *type)
  86. {
  87. struct mst_intc_chip_data *cd = d->host_data;
  88. if (is_of_node(fwspec->fwnode)) {
  89. if (fwspec->param_count != 3)
  90. return -EINVAL;
  91. /* No PPI should point to this domain */
  92. if (fwspec->param[0] != 0)
  93. return -EINVAL;
  94. if (fwspec->param[1] >= cd->nr_irqs)
  95. return -EINVAL;
  96. *hwirq = fwspec->param[1];
  97. *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
  98. return 0;
  99. }
  100. return -EINVAL;
  101. }
  102. static int mst_intc_domain_alloc(struct irq_domain *domain, unsigned int virq,
  103. unsigned int nr_irqs, void *data)
  104. {
  105. int i;
  106. irq_hw_number_t hwirq;
  107. struct irq_fwspec parent_fwspec, *fwspec = data;
  108. struct mst_intc_chip_data *cd = domain->host_data;
  109. /* Not GIC compliant */
  110. if (fwspec->param_count != 3)
  111. return -EINVAL;
  112. /* No PPI should point to this domain */
  113. if (fwspec->param[0])
  114. return -EINVAL;
  115. hwirq = fwspec->param[1];
  116. for (i = 0; i < nr_irqs; i++)
  117. irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
  118. &mst_intc_chip,
  119. domain->host_data);
  120. parent_fwspec = *fwspec;
  121. parent_fwspec.fwnode = domain->parent->fwnode;
  122. parent_fwspec.param[1] = cd->irq_start + hwirq;
  123. return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_fwspec);
  124. }
  125. static const struct irq_domain_ops mst_intc_domain_ops = {
  126. .translate = mst_intc_domain_translate,
  127. .alloc = mst_intc_domain_alloc,
  128. .free = irq_domain_free_irqs_common,
  129. };
  130. static int __init mst_intc_of_init(struct device_node *dn,
  131. struct device_node *parent)
  132. {
  133. struct irq_domain *domain, *domain_parent;
  134. struct mst_intc_chip_data *cd;
  135. u32 irq_start, irq_end;
  136. domain_parent = irq_find_host(parent);
  137. if (!domain_parent) {
  138. pr_err("mst-intc: interrupt-parent not found\n");
  139. return -EINVAL;
  140. }
  141. if (of_property_read_u32_index(dn, "mstar,irqs-map-range", 0, &irq_start) ||
  142. of_property_read_u32_index(dn, "mstar,irqs-map-range", 1, &irq_end))
  143. return -EINVAL;
  144. cd = kzalloc(sizeof(*cd), GFP_KERNEL);
  145. if (!cd)
  146. return -ENOMEM;
  147. cd->base = of_iomap(dn, 0);
  148. if (!cd->base) {
  149. kfree(cd);
  150. return -ENOMEM;
  151. }
  152. cd->no_eoi = of_property_read_bool(dn, "mstar,intc-no-eoi");
  153. raw_spin_lock_init(&cd->lock);
  154. cd->irq_start = irq_start;
  155. cd->nr_irqs = irq_end - irq_start + 1;
  156. domain = irq_domain_add_hierarchy(domain_parent, 0, cd->nr_irqs, dn,
  157. &mst_intc_domain_ops, cd);
  158. if (!domain) {
  159. iounmap(cd->base);
  160. kfree(cd);
  161. return -ENOMEM;
  162. }
  163. return 0;
  164. }
  165. IRQCHIP_DECLARE(mst_intc, "mstar,mst-intc", mst_intc_of_init);