chip.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * IRQ chip definitions for INTC IRQs.
  3. *
  4. * Copyright (C) 2007, 2008 Magnus Damm
  5. * Copyright (C) 2009, 2010 Paul Mundt
  6. *
  7. * This file is subject to the terms and conditions of the GNU General Public
  8. * License. See the file "COPYING" in the main directory of this archive
  9. * for more details.
  10. */
  11. #include <linux/cpumask.h>
  12. #include <linux/io.h>
  13. #include "internals.h"
  14. void _intc_enable(unsigned int irq, unsigned long handle)
  15. {
  16. struct intc_desc_int *d = get_intc_desc(irq);
  17. unsigned long addr;
  18. unsigned int cpu;
  19. for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
  20. #ifdef CONFIG_SMP
  21. if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
  22. continue;
  23. #endif
  24. addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
  25. intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
  26. [_INTC_FN(handle)], irq);
  27. }
  28. intc_balancing_enable(irq);
  29. }
  30. static void intc_enable(unsigned int irq)
  31. {
  32. _intc_enable(irq, (unsigned long)get_irq_chip_data(irq));
  33. }
  34. static void intc_disable(unsigned int irq)
  35. {
  36. struct intc_desc_int *d = get_intc_desc(irq);
  37. unsigned long handle = (unsigned long)get_irq_chip_data(irq);
  38. unsigned long addr;
  39. unsigned int cpu;
  40. intc_balancing_disable(irq);
  41. for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
  42. #ifdef CONFIG_SMP
  43. if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity))
  44. continue;
  45. #endif
  46. addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
  47. intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
  48. [_INTC_FN(handle)], irq);
  49. }
  50. }
  51. static int intc_set_wake(unsigned int irq, unsigned int on)
  52. {
  53. return 0; /* allow wakeup, but setup hardware in intc_suspend() */
  54. }
  55. #ifdef CONFIG_SMP
  56. /*
  57. * This is held with the irq desc lock held, so we don't require any
  58. * additional locking here at the intc desc level. The affinity mask is
  59. * later tested in the enable/disable paths.
  60. */
  61. static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask)
  62. {
  63. if (!cpumask_intersects(cpumask, cpu_online_mask))
  64. return -1;
  65. cpumask_copy(irq_to_desc(irq)->affinity, cpumask);
  66. return 0;
  67. }
  68. #endif
  69. static void intc_mask_ack(unsigned int irq)
  70. {
  71. struct intc_desc_int *d = get_intc_desc(irq);
  72. unsigned long handle = intc_get_ack_handle(irq);
  73. unsigned long addr;
  74. intc_disable(irq);
  75. /* read register and write zero only to the associated bit */
  76. if (handle) {
  77. unsigned int value;
  78. addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
  79. value = intc_set_field_from_handle(0, 1, handle);
  80. switch (_INTC_FN(handle)) {
  81. case REG_FN_MODIFY_BASE + 0: /* 8bit */
  82. __raw_readb(addr);
  83. __raw_writeb(0xff ^ value, addr);
  84. break;
  85. case REG_FN_MODIFY_BASE + 1: /* 16bit */
  86. __raw_readw(addr);
  87. __raw_writew(0xffff ^ value, addr);
  88. break;
  89. case REG_FN_MODIFY_BASE + 3: /* 32bit */
  90. __raw_readl(addr);
  91. __raw_writel(0xffffffff ^ value, addr);
  92. break;
  93. default:
  94. BUG();
  95. break;
  96. }
  97. }
  98. }
  99. static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
  100. unsigned int nr_hp,
  101. unsigned int irq)
  102. {
  103. int i;
  104. /*
  105. * this doesn't scale well, but...
  106. *
  107. * this function should only be used for cerain uncommon
  108. * operations such as intc_set_priority() and intc_set_type()
  109. * and in those rare cases performance doesn't matter that much.
  110. * keeping the memory footprint low is more important.
  111. *
  112. * one rather simple way to speed this up and still keep the
  113. * memory footprint down is to make sure the array is sorted
  114. * and then perform a bisect to lookup the irq.
  115. */
  116. for (i = 0; i < nr_hp; i++) {
  117. if ((hp + i)->irq != irq)
  118. continue;
  119. return hp + i;
  120. }
  121. return NULL;
  122. }
  123. int intc_set_priority(unsigned int irq, unsigned int prio)
  124. {
  125. struct intc_desc_int *d = get_intc_desc(irq);
  126. struct intc_handle_int *ihp;
  127. if (!intc_get_prio_level(irq) || prio <= 1)
  128. return -EINVAL;
  129. ihp = intc_find_irq(d->prio, d->nr_prio, irq);
  130. if (ihp) {
  131. if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
  132. return -EINVAL;
  133. intc_set_prio_level(irq, prio);
  134. /*
  135. * only set secondary masking method directly
  136. * primary masking method is using intc_prio_level[irq]
  137. * priority level will be set during next enable()
  138. */
  139. if (_INTC_FN(ihp->handle) != REG_FN_ERR)
  140. _intc_enable(irq, ihp->handle);
  141. }
  142. return 0;
  143. }
  144. #define VALID(x) (x | 0x80)
  145. static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
  146. [IRQ_TYPE_EDGE_FALLING] = VALID(0),
  147. [IRQ_TYPE_EDGE_RISING] = VALID(1),
  148. [IRQ_TYPE_LEVEL_LOW] = VALID(2),
  149. /* SH7706, SH7707 and SH7709 do not support high level triggered */
  150. #if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
  151. !defined(CONFIG_CPU_SUBTYPE_SH7707) && \
  152. !defined(CONFIG_CPU_SUBTYPE_SH7709)
  153. [IRQ_TYPE_LEVEL_HIGH] = VALID(3),
  154. #endif
  155. };
  156. static int intc_set_type(unsigned int irq, unsigned int type)
  157. {
  158. struct intc_desc_int *d = get_intc_desc(irq);
  159. unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
  160. struct intc_handle_int *ihp;
  161. unsigned long addr;
  162. if (!value)
  163. return -EINVAL;
  164. ihp = intc_find_irq(d->sense, d->nr_sense, irq);
  165. if (ihp) {
  166. addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
  167. intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
  168. }
  169. return 0;
  170. }
  171. struct irq_chip intc_irq_chip = {
  172. .mask = intc_disable,
  173. .unmask = intc_enable,
  174. .mask_ack = intc_mask_ack,
  175. .enable = intc_enable,
  176. .disable = intc_disable,
  177. .shutdown = intc_disable,
  178. .set_type = intc_set_type,
  179. .set_wake = intc_set_wake,
  180. #ifdef CONFIG_SMP
  181. .set_affinity = intc_set_affinity,
  182. #endif
  183. };