irq-sunxi-nmi.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Allwinner A20/A31 SoCs NMI IRQ chip driver.
  3. *
  4. * Carlo Caione <[email protected]>
  5. *
  6. * This file is licensed under the terms of the GNU General Public
  7. * License version 2. This program is licensed "as is" without any
  8. * warranty of any kind, whether express or implied.
  9. */
  10. #define DRV_NAME "sunxi-nmi"
  11. #define pr_fmt(fmt) DRV_NAME ": " fmt
  12. #include <linux/bitops.h>
  13. #include <linux/device.h>
  14. #include <linux/io.h>
  15. #include <linux/irq.h>
  16. #include <linux/interrupt.h>
  17. #include <linux/irqdomain.h>
  18. #include <linux/of_irq.h>
  19. #include <linux/of_address.h>
  20. #include <linux/of_platform.h>
  21. #include <linux/irqchip.h>
  22. #include <linux/irqchip/chained_irq.h>
  23. #define SUNXI_NMI_SRC_TYPE_MASK 0x00000003
  24. #define SUNXI_NMI_IRQ_BIT BIT(0)
  25. /*
  26. * For deprecated sun6i-a31-sc-nmi compatible.
  27. */
  28. #define SUN6I_NMI_CTRL 0x00
  29. #define SUN6I_NMI_PENDING 0x04
  30. #define SUN6I_NMI_ENABLE 0x34
  31. #define SUN7I_NMI_CTRL 0x00
  32. #define SUN7I_NMI_PENDING 0x04
  33. #define SUN7I_NMI_ENABLE 0x08
  34. #define SUN9I_NMI_CTRL 0x00
  35. #define SUN9I_NMI_ENABLE 0x04
  36. #define SUN9I_NMI_PENDING 0x08
  37. enum {
  38. SUNXI_SRC_TYPE_LEVEL_LOW = 0,
  39. SUNXI_SRC_TYPE_EDGE_FALLING,
  40. SUNXI_SRC_TYPE_LEVEL_HIGH,
  41. SUNXI_SRC_TYPE_EDGE_RISING,
  42. };
  43. struct sunxi_sc_nmi_reg_offs {
  44. u32 ctrl;
  45. u32 pend;
  46. u32 enable;
  47. };
  48. static const struct sunxi_sc_nmi_reg_offs sun6i_reg_offs __initconst = {
  49. .ctrl = SUN6I_NMI_CTRL,
  50. .pend = SUN6I_NMI_PENDING,
  51. .enable = SUN6I_NMI_ENABLE,
  52. };
  53. static const struct sunxi_sc_nmi_reg_offs sun7i_reg_offs __initconst = {
  54. .ctrl = SUN7I_NMI_CTRL,
  55. .pend = SUN7I_NMI_PENDING,
  56. .enable = SUN7I_NMI_ENABLE,
  57. };
  58. static const struct sunxi_sc_nmi_reg_offs sun9i_reg_offs __initconst = {
  59. .ctrl = SUN9I_NMI_CTRL,
  60. .pend = SUN9I_NMI_PENDING,
  61. .enable = SUN9I_NMI_ENABLE,
  62. };
  63. static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off,
  64. u32 val)
  65. {
  66. irq_reg_writel(gc, val, off);
  67. }
  68. static inline u32 sunxi_sc_nmi_read(struct irq_chip_generic *gc, u32 off)
  69. {
  70. return irq_reg_readl(gc, off);
  71. }
  72. static void sunxi_sc_nmi_handle_irq(struct irq_desc *desc)
  73. {
  74. struct irq_domain *domain = irq_desc_get_handler_data(desc);
  75. struct irq_chip *chip = irq_desc_get_chip(desc);
  76. chained_irq_enter(chip, desc);
  77. generic_handle_domain_irq(domain, 0);
  78. chained_irq_exit(chip, desc);
  79. }
  80. static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
  81. {
  82. struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data);
  83. struct irq_chip_type *ct = gc->chip_types;
  84. u32 src_type_reg;
  85. u32 ctrl_off = ct->regs.type;
  86. unsigned int src_type;
  87. unsigned int i;
  88. irq_gc_lock(gc);
  89. switch (flow_type & IRQF_TRIGGER_MASK) {
  90. case IRQ_TYPE_EDGE_FALLING:
  91. src_type = SUNXI_SRC_TYPE_EDGE_FALLING;
  92. break;
  93. case IRQ_TYPE_EDGE_RISING:
  94. src_type = SUNXI_SRC_TYPE_EDGE_RISING;
  95. break;
  96. case IRQ_TYPE_LEVEL_HIGH:
  97. src_type = SUNXI_SRC_TYPE_LEVEL_HIGH;
  98. break;
  99. case IRQ_TYPE_NONE:
  100. case IRQ_TYPE_LEVEL_LOW:
  101. src_type = SUNXI_SRC_TYPE_LEVEL_LOW;
  102. break;
  103. default:
  104. irq_gc_unlock(gc);
  105. pr_err("Cannot assign multiple trigger modes to IRQ %d.\n",
  106. data->irq);
  107. return -EBADR;
  108. }
  109. irqd_set_trigger_type(data, flow_type);
  110. irq_setup_alt_chip(data, flow_type);
  111. for (i = 0; i < gc->num_ct; i++, ct++)
  112. if (ct->type & flow_type)
  113. ctrl_off = ct->regs.type;
  114. src_type_reg = sunxi_sc_nmi_read(gc, ctrl_off);
  115. src_type_reg &= ~SUNXI_NMI_SRC_TYPE_MASK;
  116. src_type_reg |= src_type;
  117. sunxi_sc_nmi_write(gc, ctrl_off, src_type_reg);
  118. irq_gc_unlock(gc);
  119. return IRQ_SET_MASK_OK;
  120. }
  121. static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
  122. const struct sunxi_sc_nmi_reg_offs *reg_offs)
  123. {
  124. struct irq_domain *domain;
  125. struct irq_chip_generic *gc;
  126. unsigned int irq;
  127. unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
  128. int ret;
  129. domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL);
  130. if (!domain) {
  131. pr_err("Could not register interrupt domain.\n");
  132. return -ENOMEM;
  133. }
  134. ret = irq_alloc_domain_generic_chips(domain, 1, 2, DRV_NAME,
  135. handle_fasteoi_irq, clr, 0,
  136. IRQ_GC_INIT_MASK_CACHE);
  137. if (ret) {
  138. pr_err("Could not allocate generic interrupt chip.\n");
  139. goto fail_irqd_remove;
  140. }
  141. irq = irq_of_parse_and_map(node, 0);
  142. if (irq <= 0) {
  143. pr_err("unable to parse irq\n");
  144. ret = -EINVAL;
  145. goto fail_irqd_remove;
  146. }
  147. gc = irq_get_domain_generic_chip(domain, 0);
  148. gc->reg_base = of_io_request_and_map(node, 0, of_node_full_name(node));
  149. if (IS_ERR(gc->reg_base)) {
  150. pr_err("unable to map resource\n");
  151. ret = PTR_ERR(gc->reg_base);
  152. goto fail_irqd_remove;
  153. }
  154. gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
  155. gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
  156. gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
  157. gc->chip_types[0].chip.irq_eoi = irq_gc_ack_set_bit;
  158. gc->chip_types[0].chip.irq_set_type = sunxi_sc_nmi_set_type;
  159. gc->chip_types[0].chip.flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED;
  160. gc->chip_types[0].regs.ack = reg_offs->pend;
  161. gc->chip_types[0].regs.mask = reg_offs->enable;
  162. gc->chip_types[0].regs.type = reg_offs->ctrl;
  163. gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
  164. gc->chip_types[1].chip.name = gc->chip_types[0].chip.name;
  165. gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit;
  166. gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
  167. gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
  168. gc->chip_types[1].chip.irq_set_type = sunxi_sc_nmi_set_type;
  169. gc->chip_types[1].regs.ack = reg_offs->pend;
  170. gc->chip_types[1].regs.mask = reg_offs->enable;
  171. gc->chip_types[1].regs.type = reg_offs->ctrl;
  172. gc->chip_types[1].handler = handle_edge_irq;
  173. /* Disable any active interrupts */
  174. sunxi_sc_nmi_write(gc, reg_offs->enable, 0);
  175. /* Clear any pending NMI interrupts */
  176. sunxi_sc_nmi_write(gc, reg_offs->pend, SUNXI_NMI_IRQ_BIT);
  177. irq_set_chained_handler_and_data(irq, sunxi_sc_nmi_handle_irq, domain);
  178. return 0;
  179. fail_irqd_remove:
  180. irq_domain_remove(domain);
  181. return ret;
  182. }
  183. static int __init sun6i_sc_nmi_irq_init(struct device_node *node,
  184. struct device_node *parent)
  185. {
  186. return sunxi_sc_nmi_irq_init(node, &sun6i_reg_offs);
  187. }
  188. IRQCHIP_DECLARE(sun6i_sc_nmi, "allwinner,sun6i-a31-sc-nmi", sun6i_sc_nmi_irq_init);
  189. static int __init sun7i_sc_nmi_irq_init(struct device_node *node,
  190. struct device_node *parent)
  191. {
  192. return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs);
  193. }
  194. IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init);
  195. static int __init sun9i_nmi_irq_init(struct device_node *node,
  196. struct device_node *parent)
  197. {
  198. return sunxi_sc_nmi_irq_init(node, &sun9i_reg_offs);
  199. }
  200. IRQCHIP_DECLARE(sun9i_nmi, "allwinner,sun9i-a80-nmi", sun9i_nmi_irq_init);