irq-loongson-liointc.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2020, Jiaxun Yang <[email protected]>
  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. #ifdef CONFIG_MIPS
  18. #include <loongson.h>
  19. #else
  20. #include <asm/loongson.h>
  21. #endif
  22. #define LIOINTC_CHIP_IRQ 32
  23. #define LIOINTC_NUM_PARENT 4
  24. #define LIOINTC_NUM_CORES 4
  25. #define LIOINTC_INTC_CHIP_START 0x20
  26. #define LIOINTC_REG_INTC_STATUS (LIOINTC_INTC_CHIP_START + 0x20)
  27. #define LIOINTC_REG_INTC_EN_STATUS (LIOINTC_INTC_CHIP_START + 0x04)
  28. #define LIOINTC_REG_INTC_ENABLE (LIOINTC_INTC_CHIP_START + 0x08)
  29. #define LIOINTC_REG_INTC_DISABLE (LIOINTC_INTC_CHIP_START + 0x0c)
  30. #define LIOINTC_REG_INTC_POL (LIOINTC_INTC_CHIP_START + 0x10)
  31. #define LIOINTC_REG_INTC_EDGE (LIOINTC_INTC_CHIP_START + 0x14)
  32. #define LIOINTC_SHIFT_INTx 4
  33. #define LIOINTC_ERRATA_IRQ 10
  34. #if defined(CONFIG_MIPS)
  35. #define liointc_core_id get_ebase_cpunum()
  36. #else
  37. #define liointc_core_id get_csr_cpuid()
  38. #endif
  39. struct liointc_handler_data {
  40. struct liointc_priv *priv;
  41. u32 parent_int_map;
  42. };
  43. struct liointc_priv {
  44. struct irq_chip_generic *gc;
  45. struct liointc_handler_data handler[LIOINTC_NUM_PARENT];
  46. void __iomem *core_isr[LIOINTC_NUM_CORES];
  47. u8 map_cache[LIOINTC_CHIP_IRQ];
  48. bool has_lpc_irq_errata;
  49. };
  50. struct fwnode_handle *liointc_handle;
  51. static void liointc_chained_handle_irq(struct irq_desc *desc)
  52. {
  53. struct liointc_handler_data *handler = irq_desc_get_handler_data(desc);
  54. struct irq_chip *chip = irq_desc_get_chip(desc);
  55. struct irq_chip_generic *gc = handler->priv->gc;
  56. int core = liointc_core_id % LIOINTC_NUM_CORES;
  57. u32 pending;
  58. chained_irq_enter(chip, desc);
  59. pending = readl(handler->priv->core_isr[core]);
  60. if (!pending) {
  61. /* Always blame LPC IRQ if we have that bug */
  62. if (handler->priv->has_lpc_irq_errata &&
  63. (handler->parent_int_map & gc->mask_cache &
  64. BIT(LIOINTC_ERRATA_IRQ)))
  65. pending = BIT(LIOINTC_ERRATA_IRQ);
  66. else
  67. spurious_interrupt();
  68. }
  69. while (pending) {
  70. int bit = __ffs(pending);
  71. generic_handle_domain_irq(gc->domain, bit);
  72. pending &= ~BIT(bit);
  73. }
  74. chained_irq_exit(chip, desc);
  75. }
  76. static void liointc_set_bit(struct irq_chip_generic *gc,
  77. unsigned int offset,
  78. u32 mask, bool set)
  79. {
  80. if (set)
  81. writel(readl(gc->reg_base + offset) | mask,
  82. gc->reg_base + offset);
  83. else
  84. writel(readl(gc->reg_base + offset) & ~mask,
  85. gc->reg_base + offset);
  86. }
  87. static int liointc_set_type(struct irq_data *data, unsigned int type)
  88. {
  89. struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
  90. u32 mask = data->mask;
  91. unsigned long flags;
  92. irq_gc_lock_irqsave(gc, flags);
  93. switch (type) {
  94. case IRQ_TYPE_LEVEL_HIGH:
  95. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false);
  96. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
  97. break;
  98. case IRQ_TYPE_LEVEL_LOW:
  99. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false);
  100. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
  101. break;
  102. case IRQ_TYPE_EDGE_RISING:
  103. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true);
  104. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true);
  105. break;
  106. case IRQ_TYPE_EDGE_FALLING:
  107. liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true);
  108. liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false);
  109. break;
  110. default:
  111. irq_gc_unlock_irqrestore(gc, flags);
  112. return -EINVAL;
  113. }
  114. irq_gc_unlock_irqrestore(gc, flags);
  115. irqd_set_trigger_type(data, type);
  116. return 0;
  117. }
  118. static void liointc_resume(struct irq_chip_generic *gc)
  119. {
  120. struct liointc_priv *priv = gc->private;
  121. unsigned long flags;
  122. int i;
  123. irq_gc_lock_irqsave(gc, flags);
  124. /* Disable all at first */
  125. writel(0xffffffff, gc->reg_base + LIOINTC_REG_INTC_DISABLE);
  126. /* Restore map cache */
  127. for (i = 0; i < LIOINTC_CHIP_IRQ; i++)
  128. writeb(priv->map_cache[i], gc->reg_base + i);
  129. /* Restore mask cache */
  130. writel(gc->mask_cache, gc->reg_base + LIOINTC_REG_INTC_ENABLE);
  131. irq_gc_unlock_irqrestore(gc, flags);
  132. }
  133. static int parent_irq[LIOINTC_NUM_PARENT];
  134. static u32 parent_int_map[LIOINTC_NUM_PARENT];
  135. static const char *const parent_names[] = {"int0", "int1", "int2", "int3"};
  136. static const char *const core_reg_names[] = {"isr0", "isr1", "isr2", "isr3"};
  137. static int liointc_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
  138. const u32 *intspec, unsigned int intsize,
  139. unsigned long *out_hwirq, unsigned int *out_type)
  140. {
  141. if (WARN_ON(intsize < 1))
  142. return -EINVAL;
  143. *out_hwirq = intspec[0] - GSI_MIN_CPU_IRQ;
  144. *out_type = IRQ_TYPE_NONE;
  145. return 0;
  146. }
  147. static const struct irq_domain_ops acpi_irq_gc_ops = {
  148. .map = irq_map_generic_chip,
  149. .unmap = irq_unmap_generic_chip,
  150. .xlate = liointc_domain_xlate,
  151. };
  152. static int liointc_init(phys_addr_t addr, unsigned long size, int revision,
  153. struct fwnode_handle *domain_handle, struct device_node *node)
  154. {
  155. int i, err;
  156. void __iomem *base;
  157. struct irq_chip_type *ct;
  158. struct irq_chip_generic *gc;
  159. struct irq_domain *domain;
  160. struct liointc_priv *priv;
  161. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  162. if (!priv)
  163. return -ENOMEM;
  164. base = ioremap(addr, size);
  165. if (!base)
  166. goto out_free_priv;
  167. for (i = 0; i < LIOINTC_NUM_CORES; i++)
  168. priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS;
  169. for (i = 0; i < LIOINTC_NUM_PARENT; i++)
  170. priv->handler[i].parent_int_map = parent_int_map[i];
  171. if (revision > 1) {
  172. for (i = 0; i < LIOINTC_NUM_CORES; i++) {
  173. int index = of_property_match_string(node,
  174. "reg-names", core_reg_names[i]);
  175. if (index < 0)
  176. continue;
  177. priv->core_isr[i] = of_iomap(node, index);
  178. }
  179. if (!priv->core_isr[0])
  180. goto out_iounmap;
  181. }
  182. /* Setup IRQ domain */
  183. if (!acpi_disabled)
  184. domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ,
  185. &acpi_irq_gc_ops, priv);
  186. else
  187. domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ,
  188. &irq_generic_chip_ops, priv);
  189. if (!domain) {
  190. pr_err("loongson-liointc: cannot add IRQ domain\n");
  191. goto out_iounmap;
  192. }
  193. err = irq_alloc_domain_generic_chips(domain, LIOINTC_CHIP_IRQ, 1,
  194. (node ? node->full_name : "LIOINTC"),
  195. handle_level_irq, 0, IRQ_NOPROBE, 0);
  196. if (err) {
  197. pr_err("loongson-liointc: unable to register IRQ domain\n");
  198. goto out_free_domain;
  199. }
  200. /* Disable all IRQs */
  201. writel(0xffffffff, base + LIOINTC_REG_INTC_DISABLE);
  202. /* Set to level triggered */
  203. writel(0x0, base + LIOINTC_REG_INTC_EDGE);
  204. /* Generate parent INT part of map cache */
  205. for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
  206. u32 pending = priv->handler[i].parent_int_map;
  207. while (pending) {
  208. int bit = __ffs(pending);
  209. priv->map_cache[bit] = BIT(i) << LIOINTC_SHIFT_INTx;
  210. pending &= ~BIT(bit);
  211. }
  212. }
  213. for (i = 0; i < LIOINTC_CHIP_IRQ; i++) {
  214. /* Generate core part of map cache */
  215. priv->map_cache[i] |= BIT(loongson_sysconf.boot_cpu_id);
  216. writeb(priv->map_cache[i], base + i);
  217. }
  218. gc = irq_get_domain_generic_chip(domain, 0);
  219. gc->private = priv;
  220. gc->reg_base = base;
  221. gc->domain = domain;
  222. gc->resume = liointc_resume;
  223. ct = gc->chip_types;
  224. ct->regs.enable = LIOINTC_REG_INTC_ENABLE;
  225. ct->regs.disable = LIOINTC_REG_INTC_DISABLE;
  226. ct->chip.irq_unmask = irq_gc_unmask_enable_reg;
  227. ct->chip.irq_mask = irq_gc_mask_disable_reg;
  228. ct->chip.irq_mask_ack = irq_gc_mask_disable_reg;
  229. ct->chip.irq_set_type = liointc_set_type;
  230. gc->mask_cache = 0;
  231. priv->gc = gc;
  232. for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
  233. if (parent_irq[i] <= 0)
  234. continue;
  235. priv->handler[i].priv = priv;
  236. irq_set_chained_handler_and_data(parent_irq[i],
  237. liointc_chained_handle_irq, &priv->handler[i]);
  238. }
  239. liointc_handle = domain_handle;
  240. return 0;
  241. out_free_domain:
  242. irq_domain_remove(domain);
  243. out_iounmap:
  244. iounmap(base);
  245. out_free_priv:
  246. kfree(priv);
  247. return -EINVAL;
  248. }
  249. #ifdef CONFIG_OF
  250. static int __init liointc_of_init(struct device_node *node,
  251. struct device_node *parent)
  252. {
  253. bool have_parent = FALSE;
  254. int sz, i, index, revision, err = 0;
  255. struct resource res;
  256. if (!of_device_is_compatible(node, "loongson,liointc-2.0")) {
  257. index = 0;
  258. revision = 1;
  259. } else {
  260. index = of_property_match_string(node, "reg-names", "main");
  261. revision = 2;
  262. }
  263. if (of_address_to_resource(node, index, &res))
  264. return -EINVAL;
  265. for (i = 0; i < LIOINTC_NUM_PARENT; i++) {
  266. parent_irq[i] = of_irq_get_byname(node, parent_names[i]);
  267. if (parent_irq[i] > 0)
  268. have_parent = TRUE;
  269. }
  270. if (!have_parent)
  271. return -ENODEV;
  272. sz = of_property_read_variable_u32_array(node,
  273. "loongson,parent_int_map",
  274. &parent_int_map[0],
  275. LIOINTC_NUM_PARENT,
  276. LIOINTC_NUM_PARENT);
  277. if (sz < 4) {
  278. pr_err("loongson-liointc: No parent_int_map\n");
  279. return -ENODEV;
  280. }
  281. err = liointc_init(res.start, resource_size(&res),
  282. revision, of_node_to_fwnode(node), node);
  283. if (err < 0)
  284. return err;
  285. return 0;
  286. }
  287. IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init);
  288. IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init);
  289. IRQCHIP_DECLARE(loongson_liointc_2_0, "loongson,liointc-2.0", liointc_of_init);
  290. #endif
  291. #ifdef CONFIG_ACPI
  292. int __init liointc_acpi_init(struct irq_domain *parent, struct acpi_madt_lio_pic *acpi_liointc)
  293. {
  294. int ret;
  295. struct fwnode_handle *domain_handle;
  296. parent_int_map[0] = acpi_liointc->cascade_map[0];
  297. parent_int_map[1] = acpi_liointc->cascade_map[1];
  298. parent_irq[0] = irq_create_mapping(parent, acpi_liointc->cascade[0]);
  299. parent_irq[1] = irq_create_mapping(parent, acpi_liointc->cascade[1]);
  300. domain_handle = irq_domain_alloc_fwnode(&acpi_liointc->address);
  301. if (!domain_handle) {
  302. pr_err("Unable to allocate domain handle\n");
  303. return -ENOMEM;
  304. }
  305. ret = liointc_init(acpi_liointc->address, acpi_liointc->size,
  306. 1, domain_handle, NULL);
  307. if (ret)
  308. irq_domain_free_fwnode(domain_handle);
  309. return ret;
  310. }
  311. #endif