extint.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. unsigned int i = irq - sm->eim_first_irq;
  44. u32 mode, edge, level;
  45. unsigned long flags;
  46. int ret = 0;
  47. flow_type &= IRQ_TYPE_SENSE_MASK;
  48. spin_lock_irqsave(&sm->lock, flags);
  49. mode = sm_readl(sm, EIM_MODE);
  50. edge = sm_readl(sm, EIM_EDGE);
  51. level = sm_readl(sm, EIM_LEVEL);
  52. switch (flow_type) {
  53. case IRQ_TYPE_LEVEL_LOW:
  54. mode |= 1 << i;
  55. level &= ~(1 << i);
  56. break;
  57. case IRQ_TYPE_LEVEL_HIGH:
  58. mode |= 1 << i;
  59. level |= 1 << i;
  60. break;
  61. case IRQ_TYPE_EDGE_RISING:
  62. mode &= ~(1 << i);
  63. edge |= 1 << i;
  64. break;
  65. case IRQ_TYPE_EDGE_FALLING:
  66. mode &= ~(1 << i);
  67. edge &= ~(1 << i);
  68. break;
  69. default:
  70. ret = -EINVAL;
  71. break;
  72. }
  73. sm_writel(sm, EIM_MODE, mode);
  74. sm_writel(sm, EIM_EDGE, edge);
  75. sm_writel(sm, EIM_LEVEL, level);
  76. spin_unlock_irqrestore(&sm->lock, flags);
  77. return ret;
  78. }
  79. struct irq_chip eim_chip = {
  80. .name = "eim",
  81. .ack = eim_ack_irq,
  82. .mask = eim_mask_irq,
  83. .mask_ack = eim_mask_ack_irq,
  84. .unmask = eim_unmask_irq,
  85. .set_type = eim_set_irq_type,
  86. };
  87. static void demux_eim_irq(unsigned int irq, struct irq_desc *desc)
  88. {
  89. struct at32_sm *sm = desc->handler_data;
  90. struct irq_desc *ext_desc;
  91. unsigned long status, pending;
  92. unsigned int i, ext_irq;
  93. spin_lock(&sm->lock);
  94. status = sm_readl(sm, EIM_ISR);
  95. pending = status & sm_readl(sm, EIM_IMR);
  96. while (pending) {
  97. i = fls(pending) - 1;
  98. pending &= ~(1 << i);
  99. ext_irq = i + sm->eim_first_irq;
  100. ext_desc = irq_desc + ext_irq;
  101. ext_desc->handle_irq(ext_irq, ext_desc);
  102. }
  103. spin_unlock(&sm->lock);
  104. }
  105. static int __init eim_init(void)
  106. {
  107. struct at32_sm *sm = &system_manager;
  108. unsigned int i;
  109. unsigned int nr_irqs;
  110. unsigned int int_irq;
  111. u32 pattern;
  112. /*
  113. * The EIM is really the same module as SM, so register
  114. * mapping, etc. has been taken care of already.
  115. */
  116. /*
  117. * Find out how many interrupt lines that are actually
  118. * implemented in hardware.
  119. */
  120. sm_writel(sm, EIM_IDR, ~0UL);
  121. sm_writel(sm, EIM_MODE, ~0UL);
  122. pattern = sm_readl(sm, EIM_MODE);
  123. nr_irqs = fls(pattern);
  124. sm->eim_chip = &eim_chip;
  125. for (i = 0; i < nr_irqs; i++) {
  126. set_irq_chip(sm->eim_first_irq + i, &eim_chip);
  127. set_irq_chip_data(sm->eim_first_irq + i, sm);
  128. }
  129. int_irq = platform_get_irq_byname(sm->pdev, "eim");
  130. set_irq_chained_handler(int_irq, demux_eim_irq);
  131. set_irq_data(int_irq, sm);
  132. printk("EIM: External Interrupt Module at 0x%p, IRQ %u\n",
  133. sm->regs, int_irq);
  134. printk("EIM: Handling %u external IRQs, starting with IRQ %u\n",
  135. nr_irqs, sm->eim_first_irq);
  136. return 0;
  137. }
  138. arch_initcall(eim_init);