irq-gpio.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * arch/arm/mach-s5pc100/irq-gpio.c
  3. *
  4. * Copyright (C) 2009 Samsung Electronics
  5. *
  6. * S5PC100 - Interrupt handling for IRQ_GPIO${group}(x)
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2 as
  10. * published by the Free Software Foundation.
  11. */
  12. #include <linux/kernel.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/irq.h>
  15. #include <linux/io.h>
  16. #include <linux/gpio.h>
  17. #include <mach/map.h>
  18. #include <plat/gpio-cfg.h>
  19. #define S5P_GPIOREG(x) (S5P_VA_GPIO + (x))
  20. #define CON_OFFSET 0x700
  21. #define MASK_OFFSET 0x900
  22. #define PEND_OFFSET 0xA00
  23. #define CON_OFFSET_2 0xE00
  24. #define MASK_OFFSET_2 0xF00
  25. #define PEND_OFFSET_2 0xF40
  26. #define GPIOINT_LEVEL_LOW 0x0
  27. #define GPIOINT_LEVEL_HIGH 0x1
  28. #define GPIOINT_EDGE_FALLING 0x2
  29. #define GPIOINT_EDGE_RISING 0x3
  30. #define GPIOINT_EDGE_BOTH 0x4
  31. static int group_to_con_offset(int group)
  32. {
  33. return group << 2;
  34. }
  35. static int group_to_mask_offset(int group)
  36. {
  37. return group << 2;
  38. }
  39. static int group_to_pend_offset(int group)
  40. {
  41. return group << 2;
  42. }
  43. static int s5pc100_get_start(unsigned int group)
  44. {
  45. switch (group) {
  46. case 0: return S5PC100_GPIO_A0_START;
  47. case 1: return S5PC100_GPIO_A1_START;
  48. case 2: return S5PC100_GPIO_B_START;
  49. case 3: return S5PC100_GPIO_C_START;
  50. case 4: return S5PC100_GPIO_D_START;
  51. case 5: return S5PC100_GPIO_E0_START;
  52. case 6: return S5PC100_GPIO_E1_START;
  53. case 7: return S5PC100_GPIO_F0_START;
  54. case 8: return S5PC100_GPIO_F1_START;
  55. case 9: return S5PC100_GPIO_F2_START;
  56. case 10: return S5PC100_GPIO_F3_START;
  57. case 11: return S5PC100_GPIO_G0_START;
  58. case 12: return S5PC100_GPIO_G1_START;
  59. case 13: return S5PC100_GPIO_G2_START;
  60. case 14: return S5PC100_GPIO_G3_START;
  61. case 15: return S5PC100_GPIO_I_START;
  62. case 16: return S5PC100_GPIO_J0_START;
  63. case 17: return S5PC100_GPIO_J1_START;
  64. case 18: return S5PC100_GPIO_J2_START;
  65. case 19: return S5PC100_GPIO_J3_START;
  66. case 20: return S5PC100_GPIO_J4_START;
  67. default:
  68. BUG();
  69. }
  70. return -EINVAL;
  71. }
  72. static int s5pc100_get_group(unsigned int irq)
  73. {
  74. irq -= S3C_IRQ_GPIO(0);
  75. switch (irq) {
  76. case S5PC100_GPIO_A0_START ... S5PC100_GPIO_A1_START - 1:
  77. return 0;
  78. case S5PC100_GPIO_A1_START ... S5PC100_GPIO_B_START - 1:
  79. return 1;
  80. case S5PC100_GPIO_B_START ... S5PC100_GPIO_C_START - 1:
  81. return 2;
  82. case S5PC100_GPIO_C_START ... S5PC100_GPIO_D_START - 1:
  83. return 3;
  84. case S5PC100_GPIO_D_START ... S5PC100_GPIO_E0_START - 1:
  85. return 4;
  86. case S5PC100_GPIO_E0_START ... S5PC100_GPIO_E1_START - 1:
  87. return 5;
  88. case S5PC100_GPIO_E1_START ... S5PC100_GPIO_F0_START - 1:
  89. return 6;
  90. case S5PC100_GPIO_F0_START ... S5PC100_GPIO_F1_START - 1:
  91. return 7;
  92. case S5PC100_GPIO_F1_START ... S5PC100_GPIO_F2_START - 1:
  93. return 8;
  94. case S5PC100_GPIO_F2_START ... S5PC100_GPIO_F3_START - 1:
  95. return 9;
  96. case S5PC100_GPIO_F3_START ... S5PC100_GPIO_G0_START - 1:
  97. return 10;
  98. case S5PC100_GPIO_G0_START ... S5PC100_GPIO_G1_START - 1:
  99. return 11;
  100. case S5PC100_GPIO_G1_START ... S5PC100_GPIO_G2_START - 1:
  101. return 12;
  102. case S5PC100_GPIO_G2_START ... S5PC100_GPIO_G3_START - 1:
  103. return 13;
  104. case S5PC100_GPIO_G3_START ... S5PC100_GPIO_H0_START - 1:
  105. return 14;
  106. case S5PC100_GPIO_I_START ... S5PC100_GPIO_J0_START - 1:
  107. return 15;
  108. case S5PC100_GPIO_J0_START ... S5PC100_GPIO_J1_START - 1:
  109. return 16;
  110. case S5PC100_GPIO_J1_START ... S5PC100_GPIO_J2_START - 1:
  111. return 17;
  112. case S5PC100_GPIO_J2_START ... S5PC100_GPIO_J3_START - 1:
  113. return 18;
  114. case S5PC100_GPIO_J3_START ... S5PC100_GPIO_J4_START - 1:
  115. return 19;
  116. case S5PC100_GPIO_J4_START ... S5PC100_GPIO_K0_START - 1:
  117. return 20;
  118. default:
  119. BUG();
  120. }
  121. return -EINVAL;
  122. }
  123. static int s5pc100_get_offset(unsigned int irq)
  124. {
  125. struct gpio_chip *chip = get_irq_data(irq);
  126. return irq - S3C_IRQ_GPIO(chip->base);
  127. }
  128. static void s5pc100_gpioint_ack(unsigned int irq)
  129. {
  130. int group, offset, pend_offset;
  131. unsigned int value;
  132. group = s5pc100_get_group(irq);
  133. offset = s5pc100_get_offset(irq);
  134. pend_offset = group_to_pend_offset(group);
  135. value = __raw_readl(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
  136. value |= 1 << offset;
  137. __raw_writel(value, S5P_GPIOREG(PEND_OFFSET) + pend_offset);
  138. }
  139. static void s5pc100_gpioint_mask(unsigned int irq)
  140. {
  141. int group, offset, mask_offset;
  142. unsigned int value;
  143. group = s5pc100_get_group(irq);
  144. offset = s5pc100_get_offset(irq);
  145. mask_offset = group_to_mask_offset(group);
  146. value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
  147. value |= 1 << offset;
  148. __raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
  149. }
  150. static void s5pc100_gpioint_unmask(unsigned int irq)
  151. {
  152. int group, offset, mask_offset;
  153. unsigned int value;
  154. group = s5pc100_get_group(irq);
  155. offset = s5pc100_get_offset(irq);
  156. mask_offset = group_to_mask_offset(group);
  157. value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
  158. value &= ~(1 << offset);
  159. __raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
  160. }
  161. static void s5pc100_gpioint_mask_ack(unsigned int irq)
  162. {
  163. s5pc100_gpioint_mask(irq);
  164. s5pc100_gpioint_ack(irq);
  165. }
  166. static int s5pc100_gpioint_set_type(unsigned int irq, unsigned int type)
  167. {
  168. int group, offset, con_offset;
  169. unsigned int value;
  170. group = s5pc100_get_group(irq);
  171. offset = s5pc100_get_offset(irq);
  172. con_offset = group_to_con_offset(group);
  173. switch (type) {
  174. case IRQ_TYPE_NONE:
  175. printk(KERN_WARNING "No irq type\n");
  176. return -EINVAL;
  177. case IRQ_TYPE_EDGE_RISING:
  178. type = GPIOINT_EDGE_RISING;
  179. break;
  180. case IRQ_TYPE_EDGE_FALLING:
  181. type = GPIOINT_EDGE_FALLING;
  182. break;
  183. case IRQ_TYPE_EDGE_BOTH:
  184. type = GPIOINT_EDGE_BOTH;
  185. break;
  186. case IRQ_TYPE_LEVEL_HIGH:
  187. type = GPIOINT_LEVEL_HIGH;
  188. break;
  189. case IRQ_TYPE_LEVEL_LOW:
  190. type = GPIOINT_LEVEL_LOW;
  191. break;
  192. default:
  193. BUG();
  194. }
  195. value = __raw_readl(S5P_GPIOREG(CON_OFFSET) + con_offset);
  196. value &= ~(0xf << (offset * 0x4));
  197. value |= (type << (offset * 0x4));
  198. __raw_writel(value, S5P_GPIOREG(CON_OFFSET) + con_offset);
  199. return 0;
  200. }
  201. struct irq_chip s5pc100_gpioint = {
  202. .name = "GPIO",
  203. .ack = s5pc100_gpioint_ack,
  204. .mask = s5pc100_gpioint_mask,
  205. .mask_ack = s5pc100_gpioint_mask_ack,
  206. .unmask = s5pc100_gpioint_unmask,
  207. .set_type = s5pc100_gpioint_set_type,
  208. };
  209. void s5pc100_irq_gpioint_handler(unsigned int irq, struct irq_desc *desc)
  210. {
  211. int group, offset, pend_offset, mask_offset;
  212. int real_irq, group_end;
  213. unsigned int pend, mask;
  214. group_end = 21;
  215. for (group = 0; group < group_end; group++) {
  216. pend_offset = group_to_pend_offset(group);
  217. pend = __raw_readl(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
  218. if (!pend)
  219. continue;
  220. mask_offset = group_to_mask_offset(group);
  221. mask = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
  222. pend &= ~mask;
  223. for (offset = 0; offset < 8; offset++) {
  224. if (pend & (1 << offset)) {
  225. real_irq = s5pc100_get_start(group) + offset;
  226. generic_handle_irq(S3C_IRQ_GPIO(real_irq));
  227. }
  228. }
  229. }
  230. }