irq-loongson-liointc.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
  4. * Loongson Local IO Interrupt Controller support
  5. */
  6. #include <linux/errno.h>
  7. #include <linux/init.h>
  8. #include <linux/types.h>
  9. #include <linux/interrupt.h>
  10. #include <linux/ioport.h>
  11. #include <linux/irqchip.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_irq.h>
  14. #include <linux/io.h>
  15. #include <linux/smp.h>
  16. #include <linux/irqchip/chained_irq.h>
  17. #include <boot_param.h>
  18. #define LIOINTC_CHIP_IRQ 32
  19. #define LIOINTC_NUM_PARENT 4
  20. #define LIOINTC_INTC_CHIP_START 0x20
  21. #define LIOINTC_REG_INTC_STATUS (LIOINTC_INTC_CHIP_START + 0x20)
  22. #define LIOINTC_REG_INTC_EN_STATUS (LIOINTC_INTC_CHIP_START + 0x04)
  23. #define LIOINTC_REG_INTC_ENABLE (LIOINTC_INTC_CHIP_START + 0x08)
  24. #define LIOINTC_REG_INTC_DISABLE (LIOINTC_INTC_CHIP_START + 0x0c)
  25. #define LIOINTC_REG_INTC_POL (LIOINTC_INTC_CHIP_START + 0x10)
  26. #define LIOINTC_REG_INTC_EDGE (LIOINTC_INTC_CHIP_START + 0x14)
  27. #define LIOINTC_SHIFT_INTx 4
  28. #define LIOINTC_ERRATA_IRQ 10
  29. struct liointc_handler_data {
  30. struct liointc_priv *priv;
  31. u32 parent_int_map;
  32. };
  33. struct liointc_priv {
  34. struct irq_chip_generic *gc;
  35. struct liointc_handler_data handler[LIOINTC_NUM_PARENT];
  36. u8 map_cache[LIOINTC_CHIP_IRQ];
  37. bool has_lpc_irq_errata;
  38. };
  39. static void liointc_chained_handle_irq(struct irq_desc *desc)
  40. {
  41. struct liointc_handler_data *handler = irq_desc_get_handler_data(desc);
  42. struct irq_chip *chip = irq_desc_get_chip(desc);
  43. struct irq_chip_generic *gc = handler->priv->gc;
  44. u32 pending;
  45. chained_irq_enter(chip, desc);
  46. pending = readl(gc->reg_base + LIOINTC_REG_INTC_STATUS);
  47. if (!pending) {
  48. /* Always blame LPC IRQ if we have that bug */
  49. if (handler->priv->has_lpc_irq_errata &&
  50. (handler->parent_int_map & gc->mask_cache &
  51. BIT(LIOINTC_ERRATA_IRQ)))
  52. pending = BIT(LIOINTC_ERRATA_IRQ);
  53. else
  54. spurious_interrupt();
  55. }
  56. while (pending) {
  57. int bit = __ffs(pending);
  58. generic_handle_irq(irq_find_mapping(gc->domain, bit));
  59. pending &= ~BIT(bit);
  60. }
  61. chained_irq_exit(chip, desc);
  62. }
  63. static void liointc_set_bit(struct irq_chip_generic *gc,
  64. unsigned int offset,
  65. u32 mask, bool set)
  66. {
  67. if (set)
  68. writel(readl(gc->reg_base + offset) | mask,
  69. gc->reg_base + offset);
  70. else
  71. writel(readl(gc->reg_base + offset) & ~mask,
  72. gc->reg_base + offset);
  73. }
  74. static int liointc_set_type(struct irq_data *data, unsigned int type)
  75. {
  76. struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
  77. u32 mask = data->mask;
  78. unsigned long flags;
  79. irq_gc_lock_irqsave(gc, flags);
  80. switch (type) {
  81. case IRQ_TYPE_LEVEL_HIGH:
  82. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false);
  83. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
  84. break;
  85. case IRQ_TYPE_LEVEL_LOW:
  86. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false);
  87. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
  88. break;
  89. case IRQ_TYPE_EDGE_RISING:
  90. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true);
  91. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
  92. break;
  93. case IRQ_TYPE_EDGE_FALLING:
  94. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true);
  95. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
  96. break;
  97. default:
  98. irq_gc_unlock_irqrestore(gc, flags);
  99. return -EINVAL;
  100. }
  101. irq_gc_unlock_irqrestore(gc, flags);
  102. irqd_set_trigger_type(data, type);
  103. return 0;
  104. }
  105. static void liointc_resume(struct irq_chip_generic *gc)
  106. {
  107. struct liointc_priv *priv = gc->private;
  108. unsigned long flags;
  109. int i;
  110. irq_gc_lock_irqsave(gc, flags);
  111. /* Disable all at first */
  112. writel(0xffffffff, gc->reg_base + LIOINTC_REG_INTC_DISABLE);
  113. /* Restore map cache */
  114. for (i = 0; i < LIOINTC_CHIP_IRQ; i++)
  115. writeb(priv->map_cache[i], gc->reg_base + i);
  116. /* Restore mask cache */
  117. writel(gc->mask_cache, gc->reg_base + LIOINTC_REG_INTC_ENABLE);
  118. irq_gc_unlock_irqrestore(gc, flags);
  119. }
  120. static const char * const parent_names[] = {"int0", "int1", "int2", "int3"};
  121. int __init liointc_of_init(struct device_node *node,
  122. struct device_node *parent)
  123. {
  124. struct irq_chip_generic *gc;
  125. struct irq_domain *domain;
  126. struct irq_chip_type *ct;
  127. struct liointc_priv *priv;
  128. void __iomem *base;
  129. u32 of_parent_int_map[LIOINTC_NUM_PARENT];
  130. int parent_irq[LIOINTC_NUM_PARENT];
  131. bool have_parent = FALSE;
  132. int sz, i, err = 0;
  133. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  134. if (!priv)
  135. return -ENOMEM;
  136. base = of_iomap(node, 0);
  137. if (!base) {
  138. err = -ENODEV;
  139. goto out_free_priv;
  140. }
  141. for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
  142. parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
  143. if (parent_irq[i] > 0)
  144. have_parent = TRUE;
  145. }
  146. if (!have_parent) {
  147. err = -ENODEV;
  148. goto out_iounmap;
  149. }
  150. sz = of_property_read_variable_u32_array(node,
  151. "loongson,parent_int_map",
  152. &of_parent_int_map[0],
  153. LIOINTC_NUM_PARENT,
  154. LIOINTC_NUM_PARENT);
  155. if (sz < 4) {
  156. pr_err("loongson-liointc: No parent_int_map\n");
  157. err = -ENODEV;
  158. goto out_iounmap;
  159. }
  160. for (i = 0; i < LIOINTC_NUM_PARENT; i++)
  161. priv->handler[i].parent_int_map = of_parent_int_map[i];
  162. /* Setup IRQ domain */
  163. domain = irq_domain_add_linear(node, 32,
  164. &irq_generic_chip_ops, priv);
  165. if (!domain) {
  166. pr_err("loongson-liointc: cannot add IRQ domain\n");
  167. err = -EINVAL;
  168. goto out_iounmap;
  169. }
  170. err = irq_alloc_domain_generic_chips(domain, 32, 1,
  171. node->full_name, handle_level_irq,
  172. IRQ_NOPROBE, 0, 0);
  173. if (err) {
  174. pr_err("loongson-liointc: unable to register IRQ domain\n");
  175. goto out_free_domain;
  176. }
  177. /* Disable all IRQs */
  178. writel(0xffffffff, base + LIOINTC_REG_INTC_DISABLE);
  179. /* Set to level triggered */
  180. writel(0x0, base + LIOINTC_REG_INTC_EDGE);
  181. /* Generate parent INT part of map cache */
  182. for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
  183. u32 pending = priv->handler[i].parent_int_map;
  184. while (pending) {
  185. int bit = __ffs(pending);
  186. priv->map_cache[bit] = BIT(i) << LIOINTC_SHIFT_INTx;
  187. pending &= ~BIT(bit);
  188. }
  189. }
  190. for (i = 0; i < LIOINTC_CHIP_IRQ; i++) {
  191. /* Generate core part of map cache */
  192. priv->map_cache[i] |= BIT(loongson_sysconf.boot_cpu_id);
  193. writeb(priv->map_cache[i], base + i);
  194. }
  195. gc = irq_get_domain_generic_chip(domain, 0);
  196. gc->private = priv;
  197. gc->reg_base = base;
  198. gc->domain = domain;
  199. gc->resume = liointc_resume;
  200. ct = gc->chip_types;
  201. ct->regs.enable = LIOINTC_REG_INTC_ENABLE;
  202. ct->regs.disable = LIOINTC_REG_INTC_DISABLE;
  203. ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
  204. ct->chip.irq_mask = irq_gc_mask_disable_reg;
  205. ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
  206. ct->chip.irq_set_type = liointc_set_type;
  207. gc->mask_cache = 0;
  208. priv->gc = gc;
  209. for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
  210. if (parent_irq[i] <= 0)
  211. continue;
  212. priv->handler[i].priv = priv;
  213. irq_set_chained_handler_and_data(parent_irq[i],
  214. liointc_chained_handle_irq, &priv->handler[i]);
  215. }
  216. return 0;
  217. out_free_domain:
  218. irq_domain_remove(domain);
  219. out_iounmap:
  220. iounmap(base);
  221. out_free_priv:
  222. kfree(priv);
  223. return err;
  224. }
  225. IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init);
  226. IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init);