irq-sni-exiu.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Driver for Socionext External Interrupt Unit (EXIU)
  3. *
  4. * Copyright (c) 2017 Linaro, Ltd. <ard.biesheuvel@linaro.org>
  5. *
  6. * Based on irq-tegra.c:
  7. * Copyright (C) 2011 Google, Inc.
  8. * Copyright (C) 2010,2013, NVIDIA Corporation
  9. *
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License version 2 as
  12. * published by the Free Software Foundation.
  13. */
  14. #include <linux/interrupt.h>
  15. #include <linux/io.h>
  16. #include <linux/irq.h>
  17. #include <linux/irqchip.h>
  18. #include <linux/irqdomain.h>
  19. #include <linux/of.h>
  20. #include <linux/of_address.h>
  21. #include <linux/of_irq.h>
  22. #include <dt-bindings/interrupt-controller/arm-gic.h>
  23. #define NUM_IRQS 32
  24. #define EIMASK 0x00
  25. #define EISRCSEL 0x04
  26. #define EIREQSTA 0x08
  27. #define EIRAWREQSTA 0x0C
  28. #define EIREQCLR 0x10
  29. #define EILVL 0x14
  30. #define EIEDG 0x18
  31. #define EISIR 0x1C
  32. struct exiu_irq_data {
  33. void __iomem *base;
  34. u32 spi_base;
  35. };
  36. static void exiu_irq_eoi(struct irq_data *d)
  37. {
  38. struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  39. writel(BIT(d->hwirq), data->base + EIREQCLR);
  40. irq_chip_eoi_parent(d);
  41. }
  42. static void exiu_irq_mask(struct irq_data *d)
  43. {
  44. struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  45. u32 val;
  46. val = readl_relaxed(data->base + EIMASK) | BIT(d->hwirq);
  47. writel_relaxed(val, data->base + EIMASK);
  48. irq_chip_mask_parent(d);
  49. }
  50. static void exiu_irq_unmask(struct irq_data *d)
  51. {
  52. struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  53. u32 val;
  54. val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
  55. writel_relaxed(val, data->base + EIMASK);
  56. irq_chip_unmask_parent(d);
  57. }
  58. static void exiu_irq_enable(struct irq_data *d)
  59. {
  60. struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  61. u32 val;
  62. /* clear interrupts that were latched while disabled */
  63. writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
  64. val = readl_relaxed(data->base + EIMASK) & ~BIT(d->hwirq);
  65. writel_relaxed(val, data->base + EIMASK);
  66. irq_chip_enable_parent(d);
  67. }
  68. static int exiu_irq_set_type(struct irq_data *d, unsigned int type)
  69. {
  70. struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);
  71. u32 val;
  72. val = readl_relaxed(data->base + EILVL);
  73. if (type == IRQ_TYPE_EDGE_RISING || type == IRQ_TYPE_LEVEL_HIGH)
  74. val |= BIT(d->hwirq);
  75. else
  76. val &= ~BIT(d->hwirq);
  77. writel_relaxed(val, data->base + EILVL);
  78. val = readl_relaxed(data->base + EIEDG);
  79. if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
  80. val &= ~BIT(d->hwirq);
  81. else
  82. val |= BIT(d->hwirq);
  83. writel_relaxed(val, data->base + EIEDG);
  84. writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
  85. return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
  86. }
  87. static struct irq_chip exiu_irq_chip = {
  88. .name = "EXIU",
  89. .irq_eoi = exiu_irq_eoi,
  90. .irq_enable = exiu_irq_enable,
  91. .irq_mask = exiu_irq_mask,
  92. .irq_unmask = exiu_irq_unmask,
  93. .irq_set_type = exiu_irq_set_type,
  94. .irq_set_affinity = irq_chip_set_affinity_parent,
  95. .flags = IRQCHIP_SET_TYPE_MASKED |
  96. IRQCHIP_SKIP_SET_WAKE |
  97. IRQCHIP_EOI_THREADED |
  98. IRQCHIP_MASK_ON_SUSPEND,
  99. };
  100. static int exiu_domain_translate(struct irq_domain *domain,
  101. struct irq_fwspec *fwspec,
  102. unsigned long *hwirq,
  103. unsigned int *type)
  104. {
  105. struct exiu_irq_data *info = domain->host_data;
  106. if (is_of_node(fwspec->fwnode)) {
  107. if (fwspec->param_count != 3)
  108. return -EINVAL;
  109. if (fwspec->param[0] != GIC_SPI)
  110. return -EINVAL; /* No PPI should point to this domain */
  111. *hwirq = fwspec->param[1] - info->spi_base;
  112. *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
  113. return 0;
  114. }
  115. return -EINVAL;
  116. }
  117. static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq,
  118. unsigned int nr_irqs, void *data)
  119. {
  120. struct irq_fwspec *fwspec = data;
  121. struct irq_fwspec parent_fwspec;
  122. struct exiu_irq_data *info = dom->host_data;
  123. irq_hw_number_t hwirq;
  124. if (fwspec->param_count != 3)
  125. return -EINVAL; /* Not GIC compliant */
  126. if (fwspec->param[0] != GIC_SPI)
  127. return -EINVAL; /* No PPI should point to this domain */
  128. WARN_ON(nr_irqs != 1);
  129. hwirq = fwspec->param[1] - info->spi_base;
  130. irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info);
  131. parent_fwspec = *fwspec;
  132. parent_fwspec.fwnode = dom->parent->fwnode;
  133. return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec);
  134. }
  135. static const struct irq_domain_ops exiu_domain_ops = {
  136. .translate = exiu_domain_translate,
  137. .alloc = exiu_domain_alloc,
  138. .free = irq_domain_free_irqs_common,
  139. };
  140. static int __init exiu_init(struct device_node *node,
  141. struct device_node *parent)
  142. {
  143. struct irq_domain *parent_domain, *domain;
  144. struct exiu_irq_data *data;
  145. int err;
  146. if (!parent) {
  147. pr_err("%pOF: no parent, giving up\n", node);
  148. return -ENODEV;
  149. }
  150. parent_domain = irq_find_host(parent);
  151. if (!parent_domain) {
  152. pr_err("%pOF: unable to obtain parent domain\n", node);
  153. return -ENXIO;
  154. }
  155. data = kzalloc(sizeof(*data), GFP_KERNEL);
  156. if (!data)
  157. return -ENOMEM;
  158. if (of_property_read_u32(node, "socionext,spi-base", &data->spi_base)) {
  159. pr_err("%pOF: failed to parse 'spi-base' property\n", node);
  160. err = -ENODEV;
  161. goto out_free;
  162. }
  163. data->base = of_iomap(node, 0);
  164. if (!data->base) {
  165. err = -ENODEV;
  166. goto out_free;
  167. }
  168. /* clear and mask all interrupts */
  169. writel_relaxed(0xFFFFFFFF, data->base + EIREQCLR);
  170. writel_relaxed(0xFFFFFFFF, data->base + EIMASK);
  171. domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_IRQS, node,
  172. &exiu_domain_ops, data);
  173. if (!domain) {
  174. pr_err("%pOF: failed to allocate domain\n", node);
  175. err = -ENOMEM;
  176. goto out_unmap;
  177. }
  178. pr_info("%pOF: %d interrupts forwarded to %pOF\n", node, NUM_IRQS,
  179. parent);
  180. return 0;
  181. out_unmap:
  182. iounmap(data->base);
  183. out_free:
  184. kfree(data);
  185. return err;
  186. }
  187. IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_init);