irq-mtk-sysirq.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. * Copyright (c) 2014 MediaTek Inc.
  3. * Author: Joe.C <yingjoe.chen@mediatek.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. */
  14. #include <linux/irq.h>
  15. #include <linux/irqchip.h>
  16. #include <linux/irqdomain.h>
  17. #include <linux/of.h>
  18. #include <linux/of_irq.h>
  19. #include <linux/of_address.h>
  20. #include <linux/io.h>
  21. #include <linux/slab.h>
  22. #include <linux/spinlock.h>
  23. struct mtk_sysirq_chip_data {
  24. raw_spinlock_t lock;
  25. u32 nr_intpol_bases;
  26. void __iomem **intpol_bases;
  27. u32 *intpol_words;
  28. u8 *intpol_idx;
  29. u16 *which_word;
  30. };
  31. static int mtk_sysirq_set_type(struct irq_data *data, unsigned int type)
  32. {
  33. irq_hw_number_t hwirq = data->hwirq;
  34. struct mtk_sysirq_chip_data *chip_data = data->chip_data;
  35. u8 intpol_idx = chip_data->intpol_idx[hwirq];
  36. void __iomem *base;
  37. u32 offset, reg_index, value;
  38. unsigned long flags;
  39. int ret;
  40. base = chip_data->intpol_bases[intpol_idx];
  41. reg_index = chip_data->which_word[hwirq];
  42. offset = hwirq & 0x1f;
  43. raw_spin_lock_irqsave(&chip_data->lock, flags);
  44. value = readl_relaxed(base + reg_index * 4);
  45. if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING) {
  46. if (type == IRQ_TYPE_LEVEL_LOW)
  47. type = IRQ_TYPE_LEVEL_HIGH;
  48. else
  49. type = IRQ_TYPE_EDGE_RISING;
  50. value |= (1 << offset);
  51. } else {
  52. value &= ~(1 << offset);
  53. }
  54. writel_relaxed(value, base + reg_index * 4);
  55. data = data->parent_data;
  56. ret = data->chip->irq_set_type(data, type);
  57. raw_spin_unlock_irqrestore(&chip_data->lock, flags);
  58. return ret;
  59. }
  60. static struct irq_chip mtk_sysirq_chip = {
  61. .name = "MT_SYSIRQ",
  62. .irq_mask = irq_chip_mask_parent,
  63. .irq_unmask = irq_chip_unmask_parent,
  64. .irq_eoi = irq_chip_eoi_parent,
  65. .irq_set_type = mtk_sysirq_set_type,
  66. .irq_retrigger = irq_chip_retrigger_hierarchy,
  67. .irq_set_affinity = irq_chip_set_affinity_parent,
  68. };
  69. static int mtk_sysirq_domain_translate(struct irq_domain *d,
  70. struct irq_fwspec *fwspec,
  71. unsigned long *hwirq,
  72. unsigned int *type)
  73. {
  74. if (is_of_node(fwspec->fwnode)) {
  75. if (fwspec->param_count != 3)
  76. return -EINVAL;
  77. /* No PPI should point to this domain */
  78. if (fwspec->param[0] != 0)
  79. return -EINVAL;
  80. *hwirq = fwspec->param[1];
  81. *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
  82. return 0;
  83. }
  84. return -EINVAL;
  85. }
  86. static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
  87. unsigned int nr_irqs, void *arg)
  88. {
  89. int i;
  90. irq_hw_number_t hwirq;
  91. struct irq_fwspec *fwspec = arg;
  92. struct irq_fwspec gic_fwspec = *fwspec;
  93. if (fwspec->param_count != 3)
  94. return -EINVAL;
  95. /* sysirq doesn't support PPI */
  96. if (fwspec->param[0])
  97. return -EINVAL;
  98. hwirq = fwspec->param[1];
  99. for (i = 0; i < nr_irqs; i++)
  100. irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
  101. &mtk_sysirq_chip,
  102. domain->host_data);
  103. gic_fwspec.fwnode = domain->parent->fwnode;
  104. return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec);
  105. }
  106. static const struct irq_domain_ops sysirq_domain_ops = {
  107. .translate = mtk_sysirq_domain_translate,
  108. .alloc = mtk_sysirq_domain_alloc,
  109. .free = irq_domain_free_irqs_common,
  110. };
  111. static int __init mtk_sysirq_of_init(struct device_node *node,
  112. struct device_node *parent)
  113. {
  114. struct irq_domain *domain, *domain_parent;
  115. struct mtk_sysirq_chip_data *chip_data;
  116. int ret, size, intpol_num = 0, nr_intpol_bases = 0, i = 0;
  117. domain_parent = irq_find_host(parent);
  118. if (!domain_parent) {
  119. pr_err("mtk_sysirq: interrupt-parent not found\n");
  120. return -EINVAL;
  121. }
  122. chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL);
  123. if (!chip_data)
  124. return -ENOMEM;
  125. while (of_get_address(node, i++, NULL, NULL))
  126. nr_intpol_bases++;
  127. if (nr_intpol_bases == 0) {
  128. pr_err("mtk_sysirq: base address not specified\n");
  129. ret = -EINVAL;
  130. goto out_free_chip;
  131. }
  132. chip_data->intpol_words = kcalloc(nr_intpol_bases,
  133. sizeof(*chip_data->intpol_words),
  134. GFP_KERNEL);
  135. if (!chip_data->intpol_words) {
  136. ret = -ENOMEM;
  137. goto out_free_chip;
  138. }
  139. chip_data->intpol_bases = kcalloc(nr_intpol_bases,
  140. sizeof(*chip_data->intpol_bases),
  141. GFP_KERNEL);
  142. if (!chip_data->intpol_bases) {
  143. ret = -ENOMEM;
  144. goto out_free_intpol_words;
  145. }
  146. for (i = 0; i < nr_intpol_bases; i++) {
  147. struct resource res;
  148. ret = of_address_to_resource(node, i, &res);
  149. size = resource_size(&res);
  150. intpol_num += size * 8;
  151. chip_data->intpol_words[i] = size / 4;
  152. chip_data->intpol_bases[i] = of_iomap(node, i);
  153. if (ret || !chip_data->intpol_bases[i]) {
  154. pr_err("%pOF: couldn't map region %d\n", node, i);
  155. ret = -ENODEV;
  156. goto out_free_intpol;
  157. }
  158. }
  159. chip_data->intpol_idx = kcalloc(intpol_num,
  160. sizeof(*chip_data->intpol_idx),
  161. GFP_KERNEL);
  162. if (!chip_data->intpol_idx) {
  163. ret = -ENOMEM;
  164. goto out_free_intpol;
  165. }
  166. chip_data->which_word = kcalloc(intpol_num,
  167. sizeof(*chip_data->which_word),
  168. GFP_KERNEL);
  169. if (!chip_data->which_word) {
  170. ret = -ENOMEM;
  171. goto out_free_intpol_idx;
  172. }
  173. /*
  174. * assign an index of the intpol_bases for each irq
  175. * to set it fast later
  176. */
  177. for (i = 0; i < intpol_num ; i++) {
  178. u32 word = i / 32, j;
  179. for (j = 0; word >= chip_data->intpol_words[j] ; j++)
  180. word -= chip_data->intpol_words[j];
  181. chip_data->intpol_idx[i] = j;
  182. chip_data->which_word[i] = word;
  183. }
  184. domain = irq_domain_add_hierarchy(domain_parent, 0, intpol_num, node,
  185. &sysirq_domain_ops, chip_data);
  186. if (!domain) {
  187. ret = -ENOMEM;
  188. goto out_free_which_word;
  189. }
  190. raw_spin_lock_init(&chip_data->lock);
  191. return 0;
  192. out_free_which_word:
  193. kfree(chip_data->which_word);
  194. out_free_intpol_idx:
  195. kfree(chip_data->intpol_idx);
  196. out_free_intpol:
  197. for (i = 0; i < nr_intpol_bases; i++)
  198. if (chip_data->intpol_bases[i])
  199. iounmap(chip_data->intpol_bases[i]);
  200. kfree(chip_data->intpol_bases);
  201. out_free_intpol_words:
  202. kfree(chip_data->intpol_words);
  203. out_free_chip:
  204. kfree(chip_data);
  205. return ret;
  206. }
  207. IRQCHIP_DECLARE(mtk_sysirq, "mediatek,mt6577-sysirq", mtk_sysirq_of_init);