irq-loongson-pch-pic.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com>
  4. * Loongson PCH PIC support
  5. */
  6. #define pr_fmt(fmt) "pch-pic: " fmt
  7. #include <linux/interrupt.h>
  8. #include <linux/irq.h>
  9. #include <linux/irqchip.h>
  10. #include <linux/irqdomain.h>
  11. #include <linux/kernel.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/of_address.h>
  14. #include <linux/of_irq.h>
  15. #include <linux/of_platform.h>
  16. /* Registers */
  17. #define PCH_PIC_MASK 0x20
  18. #define PCH_PIC_HTMSI_EN 0x40
  19. #define PCH_PIC_EDGE 0x60
  20. #define PCH_PIC_CLR 0x80
  21. #define PCH_PIC_AUTO0 0xc0
  22. #define PCH_PIC_AUTO1 0xe0
  23. #define PCH_INT_ROUTE(irq) (0x100 + irq)
  24. #define PCH_INT_HTVEC(irq) (0x200 + irq)
  25. #define PCH_PIC_POL 0x3e0
  26. #define PIC_COUNT_PER_REG 32
  27. #define PIC_REG_COUNT 2
  28. #define PIC_COUNT (PIC_COUNT_PER_REG * PIC_REG_COUNT)
  29. #define PIC_REG_IDX(irq_id) ((irq_id) / PIC_COUNT_PER_REG)
  30. #define PIC_REG_BIT(irq_id) ((irq_id) % PIC_COUNT_PER_REG)
  31. struct pch_pic {
  32. void __iomem *base;
  33. struct irq_domain *pic_domain;
  34. u32 ht_vec_base;
  35. raw_spinlock_t pic_lock;
  36. };
  37. static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit)
  38. {
  39. u32 reg;
  40. void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
  41. raw_spin_lock(&priv->pic_lock);
  42. reg = readl(addr);
  43. reg |= BIT(PIC_REG_BIT(bit));
  44. writel(reg, addr);
  45. raw_spin_unlock(&priv->pic_lock);
  46. }
  47. static void pch_pic_bitclr(struct pch_pic *priv, int offset, int bit)
  48. {
  49. u32 reg;
  50. void __iomem *addr = priv->base + offset + PIC_REG_IDX(bit) * 4;
  51. raw_spin_lock(&priv->pic_lock);
  52. reg = readl(addr);
  53. reg &= ~BIT(PIC_REG_BIT(bit));
  54. writel(reg, addr);
  55. raw_spin_unlock(&priv->pic_lock);
  56. }
  57. static void pch_pic_mask_irq(struct irq_data *d)
  58. {
  59. struct pch_pic *priv = irq_data_get_irq_chip_data(d);
  60. pch_pic_bitset(priv, PCH_PIC_MASK, d->hwirq);
  61. irq_chip_mask_parent(d);
  62. }
  63. static void pch_pic_unmask_irq(struct irq_data *d)
  64. {
  65. struct pch_pic *priv = irq_data_get_irq_chip_data(d);
  66. writel(BIT(PIC_REG_BIT(d->hwirq)),
  67. priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
  68. irq_chip_unmask_parent(d);
  69. pch_pic_bitclr(priv, PCH_PIC_MASK, d->hwirq);
  70. }
  71. static int pch_pic_set_type(struct irq_data *d, unsigned int type)
  72. {
  73. struct pch_pic *priv = irq_data_get_irq_chip_data(d);
  74. int ret = 0;
  75. switch (type) {
  76. case IRQ_TYPE_EDGE_RISING:
  77. pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
  78. pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
  79. irq_set_handler_locked(d, handle_edge_irq);
  80. break;
  81. case IRQ_TYPE_EDGE_FALLING:
  82. pch_pic_bitset(priv, PCH_PIC_EDGE, d->hwirq);
  83. pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
  84. irq_set_handler_locked(d, handle_edge_irq);
  85. break;
  86. case IRQ_TYPE_LEVEL_HIGH:
  87. pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
  88. pch_pic_bitclr(priv, PCH_PIC_POL, d->hwirq);
  89. irq_set_handler_locked(d, handle_level_irq);
  90. break;
  91. case IRQ_TYPE_LEVEL_LOW:
  92. pch_pic_bitclr(priv, PCH_PIC_EDGE, d->hwirq);
  93. pch_pic_bitset(priv, PCH_PIC_POL, d->hwirq);
  94. irq_set_handler_locked(d, handle_level_irq);
  95. break;
  96. default:
  97. ret = -EINVAL;
  98. break;
  99. }
  100. return ret;
  101. }
  102. static void pch_pic_ack_irq(struct irq_data *d)
  103. {
  104. unsigned int reg;
  105. struct pch_pic *priv = irq_data_get_irq_chip_data(d);
  106. reg = readl(priv->base + PCH_PIC_EDGE + PIC_REG_IDX(d->hwirq) * 4);
  107. if (reg & BIT(PIC_REG_BIT(d->hwirq))) {
  108. writel(BIT(PIC_REG_BIT(d->hwirq)),
  109. priv->base + PCH_PIC_CLR + PIC_REG_IDX(d->hwirq) * 4);
  110. }
  111. irq_chip_ack_parent(d);
  112. }
  113. static struct irq_chip pch_pic_irq_chip = {
  114. .name = "PCH PIC",
  115. .irq_mask = pch_pic_mask_irq,
  116. .irq_unmask = pch_pic_unmask_irq,
  117. .irq_ack = pch_pic_ack_irq,
  118. .irq_set_affinity = irq_chip_set_affinity_parent,
  119. .irq_set_type = pch_pic_set_type,
  120. };
  121. static int pch_pic_alloc(struct irq_domain *domain, unsigned int virq,
  122. unsigned int nr_irqs, void *arg)
  123. {
  124. int err;
  125. unsigned int type;
  126. unsigned long hwirq;
  127. struct irq_fwspec *fwspec = arg;
  128. struct irq_fwspec parent_fwspec;
  129. struct pch_pic *priv = domain->host_data;
  130. err = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
  131. if (err)
  132. return err;
  133. parent_fwspec.fwnode = domain->parent->fwnode;
  134. parent_fwspec.param_count = 1;
  135. parent_fwspec.param[0] = hwirq + priv->ht_vec_base;
  136. err = irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
  137. if (err)
  138. return err;
  139. irq_domain_set_info(domain, virq, hwirq,
  140. &pch_pic_irq_chip, priv,
  141. handle_level_irq, NULL, NULL);
  142. irq_set_probe(virq);
  143. return 0;
  144. }
  145. static const struct irq_domain_ops pch_pic_domain_ops = {
  146. .translate = irq_domain_translate_twocell,
  147. .alloc = pch_pic_alloc,
  148. .free = irq_domain_free_irqs_parent,
  149. };
  150. static void pch_pic_reset(struct pch_pic *priv)
  151. {
  152. int i;
  153. for (i = 0; i < PIC_COUNT; i++) {
  154. /* Write vectore ID */
  155. writeb(priv->ht_vec_base + i, priv->base + PCH_INT_HTVEC(i));
  156. /* Hardcode route to HT0 Lo */
  157. writeb(1, priv->base + PCH_INT_ROUTE(i));
  158. }
  159. for (i = 0; i < PIC_REG_COUNT; i++) {
  160. /* Clear IRQ cause registers, mask all interrupts */
  161. writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_MASK + 4 * i);
  162. writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_CLR + 4 * i);
  163. /* Clear auto bounce, we don't need that */
  164. writel_relaxed(0, priv->base + PCH_PIC_AUTO0 + 4 * i);
  165. writel_relaxed(0, priv->base + PCH_PIC_AUTO1 + 4 * i);
  166. /* Enable HTMSI transformer */
  167. writel_relaxed(0xFFFFFFFF, priv->base + PCH_PIC_HTMSI_EN + 4 * i);
  168. }
  169. }
  170. static int pch_pic_of_init(struct device_node *node,
  171. struct device_node *parent)
  172. {
  173. struct pch_pic *priv;
  174. struct irq_domain *parent_domain;
  175. int err;
  176. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  177. if (!priv)
  178. return -ENOMEM;
  179. raw_spin_lock_init(&priv->pic_lock);
  180. priv->base = of_iomap(node, 0);
  181. if (!priv->base) {
  182. err = -ENOMEM;
  183. goto free_priv;
  184. }
  185. parent_domain = irq_find_host(parent);
  186. if (!parent_domain) {
  187. pr_err("Failed to find the parent domain\n");
  188. err = -ENXIO;
  189. goto iounmap_base;
  190. }
  191. if (of_property_read_u32(node, "loongson,pic-base-vec",
  192. &priv->ht_vec_base)) {
  193. pr_err("Failed to determine pic-base-vec\n");
  194. err = -EINVAL;
  195. goto iounmap_base;
  196. }
  197. priv->pic_domain = irq_domain_create_hierarchy(parent_domain, 0,
  198. PIC_COUNT,
  199. of_node_to_fwnode(node),
  200. &pch_pic_domain_ops,
  201. priv);
  202. if (!priv->pic_domain) {
  203. pr_err("Failed to create IRQ domain\n");
  204. err = -ENOMEM;
  205. goto iounmap_base;
  206. }
  207. pch_pic_reset(priv);
  208. return 0;
  209. iounmap_base:
  210. iounmap(priv->base);
  211. free_priv:
  212. kfree(priv);
  213. return err;
  214. }
  215. IRQCHIP_DECLARE(pch_pic, "loongson,pch-pic-1.0", pch_pic_of_init);