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 static struct s3c_gpio_chip
*irq_chips
[S5P_GPIOINT_GROUP_MAXNR
];
33 static int s5p_gpioint_get_group(struct irq_data
*data
)
35 struct gpio_chip
*chip
= irq_data_get_irq_data(data
);
36 struct s3c_gpio_chip
*s3c_chip
= container_of(chip
,
37 struct s3c_gpio_chip
, chip
);
40 for (group
= 0; group
< S5P_GPIOINT_GROUP_MAXNR
; group
++)
41 if (s3c_chip
== irq_chips
[group
])
47 static int s5p_gpioint_get_offset(struct irq_data
*data
)
49 struct gpio_chip
*chip
= irq_data_get_irq_data(data
);
50 struct s3c_gpio_chip
*s3c_chip
= container_of(chip
,
51 struct s3c_gpio_chip
, chip
);
53 return data
->irq
- s3c_chip
->irq_base
;
56 static void s5p_gpioint_ack(struct irq_data
*data
)
58 int group
, offset
, pend_offset
;
61 group
= s5p_gpioint_get_group(data
);
62 offset
= s5p_gpioint_get_offset(data
);
63 pend_offset
= group
<< 2;
65 value
= __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET
) + pend_offset
);
67 __raw_writel(value
, S5P_GPIOREG(GPIOINT_PEND_OFFSET
) + pend_offset
);
70 static void s5p_gpioint_mask(struct irq_data
*data
)
72 int group
, offset
, mask_offset
;
75 group
= s5p_gpioint_get_group(data
);
76 offset
= s5p_gpioint_get_offset(data
);
77 mask_offset
= group
<< 2;
79 value
= __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
81 __raw_writel(value
, S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
84 static void s5p_gpioint_unmask(struct irq_data
*data
)
86 int group
, offset
, mask_offset
;
89 group
= s5p_gpioint_get_group(data
);
90 offset
= s5p_gpioint_get_offset(data
);
91 mask_offset
= group
<< 2;
93 value
= __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
94 value
&= ~(1 << offset
);
95 __raw_writel(value
, S5P_GPIOREG(GPIOINT_MASK_OFFSET
) + mask_offset
);
98 static void s5p_gpioint_mask_ack(struct irq_data
*data
)
100 s5p_gpioint_mask(data
);
101 s5p_gpioint_ack(data
);
104 static int s5p_gpioint_set_type(struct irq_data
*data
, unsigned int type
)
106 int group
, offset
, con_offset
;
109 group
= s5p_gpioint_get_group(data
);
110 offset
= s5p_gpioint_get_offset(data
);
111 con_offset
= group
<< 2;
114 case IRQ_TYPE_EDGE_RISING
:
115 type
= S5P_IRQ_TYPE_EDGE_RISING
;
117 case IRQ_TYPE_EDGE_FALLING
:
118 type
= S5P_IRQ_TYPE_EDGE_FALLING
;
120 case IRQ_TYPE_EDGE_BOTH
:
121 type
= S5P_IRQ_TYPE_EDGE_BOTH
;
123 case IRQ_TYPE_LEVEL_HIGH
:
124 type
= S5P_IRQ_TYPE_LEVEL_HIGH
;
126 case IRQ_TYPE_LEVEL_LOW
:
127 type
= S5P_IRQ_TYPE_LEVEL_LOW
;
131 printk(KERN_WARNING
"No irq type\n");
135 value
= __raw_readl(S5P_GPIOREG(GPIOINT_CON_OFFSET
) + con_offset
);
136 value
&= ~(0x7 << (offset
* 0x4));
137 value
|= (type
<< (offset
* 0x4));
138 __raw_writel(value
, S5P_GPIOREG(GPIOINT_CON_OFFSET
) + con_offset
);
143 struct irq_chip s5p_gpioint
= {
144 .name
= "s5p_gpioint",
145 .irq_ack
= s5p_gpioint_ack
,
146 .irq_mask
= s5p_gpioint_mask
,
147 .irq_mask_ack
= s5p_gpioint_mask_ack
,
148 .irq_unmask
= s5p_gpioint_unmask
,
149 .irq_set_type
= s5p_gpioint_set_type
,
152 static void s5p_gpioint_handler(unsigned int irq
, struct irq_desc
*desc
)
154 int group
, offset
, pend_offset
, mask_offset
;
156 unsigned int pend
, mask
;
158 for (group
= 0; group
< S5P_GPIOINT_GROUP_MAXNR
; group
++) {
159 pend_offset
= group
<< 2;
160 pend
= __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET
) +
165 mask_offset
= group
<< 2;
166 mask
= __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET
) +
170 for (offset
= 0; offset
< 8; offset
++) {
171 if (pend
& (1 << offset
)) {
172 struct s3c_gpio_chip
*chip
= irq_chips
[group
];
174 real_irq
= chip
->irq_base
+ offset
;
175 generic_handle_irq(real_irq
);
182 static __init
int s5p_gpioint_add(struct s3c_gpio_chip
*chip
)
184 static int used_gpioint_groups
= 0;
185 static bool handler_registered
= 0;
186 int irq
, group
= chip
->group
;
189 if (used_gpioint_groups
>= S5P_GPIOINT_GROUP_COUNT
)
192 chip
->irq_base
= S5P_GPIOINT_BASE
+
193 used_gpioint_groups
* S5P_GPIOINT_GROUP_SIZE
;
194 used_gpioint_groups
++;
196 if (!handler_registered
) {
197 set_irq_chained_handler(IRQ_GPIOINT
, s5p_gpioint_handler
);
198 handler_registered
= 1;
201 irq_chips
[group
] = chip
;
202 for (i
= 0; i
< chip
->chip
.ngpio
; i
++) {
203 irq
= chip
->irq_base
+ i
;
204 set_irq_chip(irq
, &s5p_gpioint
);
205 set_irq_data(irq
, &chip
->chip
);
206 set_irq_handler(irq
, handle_level_irq
);
207 set_irq_flags(irq
, IRQF_VALID
);
212 int __init
s5p_register_gpio_interrupt(int pin
)
214 struct s3c_gpio_chip
*my_chip
= s3c_gpiolib_getchip(pin
);
221 offset
= pin
- my_chip
->chip
.base
;
222 group
= my_chip
->group
;
224 /* check if the group has been already registered */
225 if (my_chip
->irq_base
)
226 return my_chip
->irq_base
+ offset
;
228 /* register gpio group */
229 ret
= s5p_gpioint_add(my_chip
);
231 my_chip
->chip
.to_irq
= samsung_gpiolib_to_irq
;
232 printk(KERN_INFO
"Registered interrupt support for gpio group %d.\n",
234 return my_chip
->irq_base
+ offset
;