extint.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * External interrupt handling for AT32AP CPUs
  3. *
  4. * Copyright (C) 2006 Atmel Corporation
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #include <linux/errno.h>
  11. #include <linux/init.h>
  12. #include <linux/interrupt.h>
  13. #include <linux/irq.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/random.h>
  16. #include <asm/io.h>
  17. #include <asm/arch/sm.h>
  18. #include "sm.h"
  19. static void eim_ack_irq(unsigned int irq)
  20. {
  21. struct at32_sm *sm = get_irq_chip_data(irq);
  22. sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq));
  23. }
  24. static void eim_mask_irq(unsigned int irq)
  25. {
  26. struct at32_sm *sm = get_irq_chip_data(irq);
  27. sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq));
  28. }
  29. static void eim_mask_ack_irq(unsigned int irq)
  30. {
  31. struct at32_sm *sm = get_irq_chip_data(irq);
  32. sm_writel(sm, EIM_ICR, 1 << (irq - sm->eim_first_irq));
  33. sm_writel(sm, EIM_IDR, 1 << (irq - sm->eim_first_irq));
  34. }
  35. static void eim_unmask_irq(unsigned int irq)
  36. {
  37. struct at32_sm *sm = get_irq_chip_data(irq);
  38. sm_writel(sm, EIM_IER, 1 << (irq - sm->eim_first_irq));
  39. }
  40. static int eim_set_irq_type(unsigned int irq, unsigned int flow_type)
  41. {
  42. struct at32_sm *sm = get_irq_chip_data(irq);
  43. struct irq_desc *desc;
  44. unsigned int i = irq - sm->eim_first_irq;
  45. u32 mode, edge, level;
  46. unsigned long flags;
  47. int ret = 0;
  48. if (flow_type == IRQ_TYPE_NONE)
  49. flow_type = IRQ_TYPE_LEVEL_LOW;
  50. desc = &irq_desc[irq];
  51. desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
  52. desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
  53. if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
  54. desc->status |= IRQ_LEVEL;
  55. set_irq_handler(irq, handle_level_irq);
  56. } else {
  57. set_irq_handler(irq, handle_edge_irq);
  58. }
  59. spin_lock_irqsave(&sm->lock, flags);
  60. mode = sm_readl(sm, EIM_MODE);
  61. edge = sm_readl(sm, EIM_EDGE);
  62. level = sm_readl(sm, EIM_LEVEL);
  63. switch (flow_type) {
  64. case IRQ_TYPE_LEVEL_LOW:
  65. mode |= 1 << i;
  66. level &= ~(1 << i);
  67. break;
  68. case IRQ_TYPE_LEVEL_HIGH:
  69. mode |= 1 << i;
  70. level |= 1 << i;
  71. break;
  72. case IRQ_TYPE_EDGE_RISING:
  73. mode &= ~(1 << i);
  74. edge |= 1 << i;
  75. break;
  76. case IRQ_TYPE_EDGE_FALLING:
  77. mode &= ~(1 << i);
  78. edge &= ~(1 << i);
  79. break;
  80. default:
  81. ret = -EINVAL;
  82. break;
  83. }
  84. sm_writel(sm, EIM_MODE, mode);
  85. sm_writel(sm, EIM_EDGE, edge);
  86. sm_writel(sm, EIM_LEVEL, level);
  87. spin_unlock_irqrestore(&sm->lock, flags);
  88. return ret;
  89. }
  90. struct irq_chip eim_chip = {
  91. .name = "eim",
  92. .ack = eim_ack_irq,
  93. .mask = eim_mask_irq,
  94. .mask_ack = eim_mask_ack_irq,
  95. .unmask = eim_unmask_irq,
  96. .set_type = eim_set_irq_type,
  97. };
  98. static void demux_eim_irq(unsigned int irq, struct irq_desc *desc)
  99. {
  100. struct at32_sm *sm = desc->handler_data;
  101. struct irq_desc *ext_desc;
  102. unsigned long status, pending;
  103. unsigned int i, ext_irq;
  104. spin_lock(&sm->lock);
  105. status = sm_readl(sm, EIM_ISR);
  106. pending = status & sm_readl(sm, EIM_IMR);
  107. while (pending) {
  108. i = fls(pending) - 1;
  109. pending &= ~(1 << i);
  110. ext_irq = i + sm->eim_first_irq;
  111. ext_desc = irq_desc + ext_irq;
  112. ext_desc->handle_irq(ext_irq, ext_desc);
  113. }
  114. spin_unlock(&sm->lock);
  115. }
  116. static int __init eim_init(void)
  117. {
  118. struct at32_sm *sm = &system_manager;
  119. unsigned int i;
  120. unsigned int nr_irqs;
  121. unsigned int int_irq;
  122. u32 pattern;
  123. /*
  124. * The EIM is really the same module as SM, so register
  125. * mapping, etc. has been taken care of already.
  126. */
  127. /*
  128. * Find out how many interrupt lines that are actually
  129. * implemented in hardware.
  130. */
  131. sm_writel(sm, EIM_IDR, ~0UL);
  132. sm_writel(sm, EIM_MODE, ~0UL);
  133. pattern = sm_readl(sm, EIM_MODE);
  134. nr_irqs = fls(pattern);
  135. /* Trigger on falling edge unless overridden by driver */
  136. sm_writel(sm, EIM_MODE, 0UL);
  137. sm_writel(sm, EIM_EDGE, 0UL);
  138. sm->eim_chip = &eim_chip;
  139. for (i = 0; i < nr_irqs; i++) {
  140. set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip,
  141. handle_edge_irq);
  142. set_irq_chip_data(sm->eim_first_irq + i, sm);
  143. }
  144. int_irq = platform_get_irq_byname(sm->pdev, "eim");
  145. set_irq_chained_handler(int_irq, demux_eim_irq);
  146. set_irq_data(int_irq, sm);
  147. printk("EIM: External Interrupt Module at 0x%p, IRQ %u\n",
  148. sm->regs, int_irq);
  149. printk("EIM: Handling %u external IRQs, starting with IRQ %u\n",
  150. nr_irqs, sm->eim_first_irq);
  151. return 0;
  152. }
  153. arch_initcall(eim_init);