1 /* linux/arch/arm/plat-s5p/irq-gpioint.c
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * Author: Kyungmin Park <kyungmin.park@samsung.com>
5 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
6 * Author: Marek Szyprowski <m.szyprowski@samsung.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
15 #include <linux/kernel.h>
16 #include <linux/interrupt.h>
17 #include <linux/irq.h>
19 #include <linux/gpio.h>
22 #include <plat/gpio-core.h>
23 #include <plat/gpio-cfg.h>
25 #define S5P_GPIOREG(x) (S5P_VA_GPIO + (x))
27 #define GPIOINT_CON_OFFSET 0x700
28 #define GPIOINT_MASK_OFFSET 0x900
29 #define GPIOINT_PEND_OFFSET 0xA00
31 #define GPIOINT_LEVEL_LOW 0x0
32 #define GPIOINT_LEVEL_HIGH 0x1
33 #define GPIOINT_EDGE_FALLING 0x2
34 #define GPIOINT_EDGE_RISING 0x3
35 #define GPIOINT_EDGE_BOTH 0x4
37 static struct s3c_gpio_chip
*irq_chips
[S5P_GPIOINT_GROUP_MAXNR
];
39 static int s5p_gpioint_get_group(unsigned int irq
)
41 struct gpio_chip
*chip
= get_irq_data(irq
);
42 struct s3c_gpio_chip
*s3c_chip
= container_of(chip
,
43 struct s3c_gpio_chip
, chip
);
46 for (group
= 0; group
< S5P_GPIOINT_GROUP_MAXNR
; group
++)
47 if (s3c_chip
== irq_chips
[group
])
53 static int s5p_gpioint_get_offset(unsigned int irq
)
55 struct gpio_chip
*chip
= get_irq_data(irq
);
56 struct s3c_gpio_chip
*s3c_chip
= container_of(chip
,
57 struct s3c_gpio_chip
, chip
);
59 return irq
- s3c_chip
->irq_base
;
62 static void s5p_gpioint_ack(unsigned int irq
)
64 int group
, offset
, pend_offset
;
67 group
= s5p_gpioint_get_group(irq
);
68 offset
= s5p_gpioint_get_offset(irq
);
69 pend_offset
= group
<< 2;
71 value
= __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET
) + pend_offset
);
73 __raw_writel(value
, S5P_GPIOREG(GPIOINT_PEND_OFFSET
) + pend_offset
);
76 static void s5p_gpioint_mask(unsigned int irq
)
78 int group
, offset
, mask_offset
;
81 group
= s5p_gpioint_get_group(irq
);
82 offset
= s5p_gpioint_get_offset(irq
);
83 mask_offset
= group
<< 2;
85 value
= __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
87 __raw_writel(value
, S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
90 static void s5p_gpioint_unmask(unsigned int irq
)
92 int group
, offset
, mask_offset
;
95 group
= s5p_gpioint_get_group(irq
);
96 offset
= s5p_gpioint_get_offset(irq
);
97 mask_offset
= group
<< 2;
99 value
= __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
100 value
&= ~(1 << offset
);
101 __raw_writel(value
, S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
104 static void s5p_gpioint_mask_ack(unsigned int irq
)
106 s5p_gpioint_mask(irq
);
107 s5p_gpioint_ack(irq
);
110 static int s5p_gpioint_set_type(unsigned int irq
, unsigned int type
)
112 int group
, offset
, con_offset
;
115 group
= s5p_gpioint_get_group(irq
);
116 offset
= s5p_gpioint_get_offset(irq
);
117 con_offset
= group
<< 2;
120 case IRQ_TYPE_EDGE_RISING
:
121 type
= GPIOINT_EDGE_RISING
;
123 case IRQ_TYPE_EDGE_FALLING
:
124 type
= GPIOINT_EDGE_FALLING
;
126 case IRQ_TYPE_EDGE_BOTH
:
127 type
= GPIOINT_EDGE_BOTH
;
129 case IRQ_TYPE_LEVEL_HIGH
:
130 type
= GPIOINT_LEVEL_HIGH
;
132 case IRQ_TYPE_LEVEL_LOW
:
133 type
= GPIOINT_LEVEL_LOW
;
137 printk(KERN_WARNING
"No irq type\n");
141 value
= __raw_readl(S5P_GPIOREG(GPIOINT_CON_OFFSET
) + con_offset
);
142 value
&= ~(0x7 << (offset
* 0x4));
143 value
|= (type
<< (offset
* 0x4));
144 __raw_writel(value
, S5P_GPIOREG(GPIOINT_CON_OFFSET
) + con_offset
);
149 struct irq_chip s5p_gpioint
= {
150 .name
= "s5p_gpioint",
151 .ack
= s5p_gpioint_ack
,
152 .mask
= s5p_gpioint_mask
,
153 .mask_ack
= s5p_gpioint_mask_ack
,
154 .unmask
= s5p_gpioint_unmask
,
155 .set_type
= s5p_gpioint_set_type
,
158 static void s5p_gpioint_handler(unsigned int irq
, struct irq_desc
*desc
)
160 int group
, offset
, pend_offset
, mask_offset
;
162 unsigned int pend
, mask
;
164 for (group
= 0; group
< S5P_GPIOINT_GROUP_MAXNR
; group
++) {
165 pend_offset
= group
<< 2;
166 pend
= __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET
) +
171 mask_offset
= group
<< 2;
172 mask
= __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET
) +
176 for (offset
= 0; offset
< 8; offset
++) {
177 if (pend
& (1 << offset
)) {
178 struct s3c_gpio_chip
*chip
= irq_chips
[group
];
180 real_irq
= chip
->irq_base
+ offset
;
181 generic_handle_irq(real_irq
);
188 static __init
int s5p_gpioint_add(struct s3c_gpio_chip
*chip
)
190 static int used_gpioint_groups
= 0;
191 static bool handler_registered
= 0;
192 int irq
, group
= chip
->group
;
195 if (used_gpioint_groups
>= S5P_GPIOINT_GROUP_COUNT
)
198 chip
->irq_base
= S5P_GPIOINT_BASE
+
199 used_gpioint_groups
* S5P_GPIOINT_GROUP_SIZE
;
200 used_gpioint_groups
++;
202 if (!handler_registered
) {
203 set_irq_chained_handler(IRQ_GPIOINT
, s5p_gpioint_handler
);
204 handler_registered
= 1;
207 irq_chips
[group
] = chip
;
208 for (i
= 0; i
< chip
->chip
.ngpio
; i
++) {
209 irq
= chip
->irq_base
+ i
;
210 set_irq_chip(irq
, &s5p_gpioint
);
211 set_irq_data(irq
, &chip
->chip
);
212 set_irq_handler(irq
, handle_level_irq
);
213 set_irq_flags(irq
, IRQF_VALID
);
218 int __init
s5p_register_gpio_interrupt(int pin
)
220 struct s3c_gpio_chip
*my_chip
= s3c_gpiolib_getchip(pin
);
227 offset
= pin
- my_chip
->chip
.base
;
228 group
= my_chip
->group
;
230 /* check if the group has been already registered */
231 if (my_chip
->irq_base
)
232 return my_chip
->irq_base
+ offset
;
234 /* register gpio group */
235 ret
= s5p_gpioint_add(my_chip
);
237 printk(KERN_INFO
"Registered interrupt support for gpio group %d.\n",
239 return my_chip
->irq_base
+ offset
;