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>
20 #include <linux/slab.h>
23 #include <plat/gpio-core.h>
24 #include <plat/gpio-cfg.h>
26 #define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u)
28 #define CON_OFFSET 0x700
29 #define MASK_OFFSET 0x900
30 #define PEND_OFFSET 0xA00
31 #define REG_OFFSET(x) ((x) << 2)
33 struct s5p_gpioint_bank
{
34 struct list_head list
;
38 struct s3c_gpio_chip
**chips
;
39 void (*handler
)(unsigned int, struct irq_desc
*);
44 static int s5p_gpioint_get_offset(struct irq_data
*data
)
46 struct s3c_gpio_chip
*chip
= irq_data_get_irq_data(data
);
47 return data
->irq
- chip
->irq_base
;
50 static void s5p_gpioint_ack(struct irq_data
*data
)
52 struct s3c_gpio_chip
*chip
= irq_data_get_irq_data(data
);
53 int group
, offset
, pend_offset
;
57 offset
= s5p_gpioint_get_offset(data
);
58 pend_offset
= REG_OFFSET(group
);
60 value
= __raw_readl(GPIO_BASE(chip
) + PEND_OFFSET
+ pend_offset
);
62 __raw_writel(value
, GPIO_BASE(chip
) + PEND_OFFSET
+ pend_offset
);
65 static void s5p_gpioint_mask(struct irq_data
*data
)
67 struct s3c_gpio_chip
*chip
= irq_data_get_irq_data(data
);
68 int group
, offset
, mask_offset
;
72 offset
= s5p_gpioint_get_offset(data
);
73 mask_offset
= REG_OFFSET(group
);
75 value
= __raw_readl(GPIO_BASE(chip
) + MASK_OFFSET
+ mask_offset
);
77 __raw_writel(value
, GPIO_BASE(chip
) + MASK_OFFSET
+ mask_offset
);
80 static void s5p_gpioint_unmask(struct irq_data
*data
)
82 struct s3c_gpio_chip
*chip
= irq_data_get_irq_data(data
);
83 int group
, offset
, mask_offset
;
87 offset
= s5p_gpioint_get_offset(data
);
88 mask_offset
= REG_OFFSET(group
);
90 value
= __raw_readl(GPIO_BASE(chip
) + MASK_OFFSET
+ mask_offset
);
91 value
&= ~BIT(offset
);
92 __raw_writel(value
, GPIO_BASE(chip
) + MASK_OFFSET
+ mask_offset
);
95 static void s5p_gpioint_mask_ack(struct irq_data
*data
)
97 s5p_gpioint_mask(data
);
98 s5p_gpioint_ack(data
);
101 static int s5p_gpioint_set_type(struct irq_data
*data
, unsigned int type
)
103 struct s3c_gpio_chip
*chip
= irq_data_get_irq_data(data
);
104 int group
, offset
, con_offset
;
108 offset
= s5p_gpioint_get_offset(data
);
109 con_offset
= REG_OFFSET(group
);
112 case IRQ_TYPE_EDGE_RISING
:
113 type
= S5P_IRQ_TYPE_EDGE_RISING
;
115 case IRQ_TYPE_EDGE_FALLING
:
116 type
= S5P_IRQ_TYPE_EDGE_FALLING
;
118 case IRQ_TYPE_EDGE_BOTH
:
119 type
= S5P_IRQ_TYPE_EDGE_BOTH
;
121 case IRQ_TYPE_LEVEL_HIGH
:
122 type
= S5P_IRQ_TYPE_LEVEL_HIGH
;
124 case IRQ_TYPE_LEVEL_LOW
:
125 type
= S5P_IRQ_TYPE_LEVEL_LOW
;
129 printk(KERN_WARNING
"No irq type\n");
133 value
= __raw_readl(GPIO_BASE(chip
) + CON_OFFSET
+ con_offset
);
134 value
&= ~(0x7 << (offset
* 0x4));
135 value
|= (type
<< (offset
* 0x4));
136 __raw_writel(value
, GPIO_BASE(chip
) + CON_OFFSET
+ con_offset
);
141 static struct irq_chip s5p_gpioint
= {
142 .name
= "s5p_gpioint",
143 .irq_ack
= s5p_gpioint_ack
,
144 .irq_mask
= s5p_gpioint_mask
,
145 .irq_mask_ack
= s5p_gpioint_mask_ack
,
146 .irq_unmask
= s5p_gpioint_unmask
,
147 .irq_set_type
= s5p_gpioint_set_type
,
150 static void s5p_gpioint_handler(unsigned int irq
, struct irq_desc
*desc
)
152 struct s5p_gpioint_bank
*bank
= get_irq_data(irq
);
153 int group
, pend_offset
, mask_offset
;
154 unsigned int pend
, mask
;
156 for (group
= 0; group
< bank
->nr_groups
; group
++) {
157 struct s3c_gpio_chip
*chip
= bank
->chips
[group
];
161 pend_offset
= REG_OFFSET(group
);
162 pend
= __raw_readl(GPIO_BASE(chip
) + PEND_OFFSET
+ pend_offset
);
166 mask_offset
= REG_OFFSET(group
);
167 mask
= __raw_readl(GPIO_BASE(chip
) + MASK_OFFSET
+ mask_offset
);
171 int offset
= fls(pend
) - 1;
172 int real_irq
= chip
->irq_base
+ offset
;
173 generic_handle_irq(real_irq
);
174 pend
&= ~BIT(offset
);
179 static __init
int s5p_gpioint_add(struct s3c_gpio_chip
*chip
)
181 static int used_gpioint_groups
= 0;
182 int irq
, group
= chip
->group
;
184 struct s5p_gpioint_bank
*bank
= NULL
;
186 if (used_gpioint_groups
>= S5P_GPIOINT_GROUP_COUNT
)
189 list_for_each_entry(bank
, &banks
, list
) {
190 if (group
>= bank
->start
&&
191 group
< bank
->start
+ bank
->nr_groups
)
197 if (!bank
->handler
) {
198 bank
->chips
= kzalloc(sizeof(struct s3c_gpio_chip
*) *
199 bank
->nr_groups
, GFP_KERNEL
);
203 set_irq_chained_handler(bank
->irq
, s5p_gpioint_handler
);
204 set_irq_data(bank
->irq
, bank
);
205 bank
->handler
= s5p_gpioint_handler
;
206 printk(KERN_INFO
"Registered chained gpio int handler for interrupt %d.\n",
211 * chained GPIO irq has been sucessfully registered, allocate new gpio
212 * int group and assign irq nubmers
215 chip
->irq_base
= S5P_GPIOINT_BASE
+
216 used_gpioint_groups
* S5P_GPIOINT_GROUP_SIZE
;
217 used_gpioint_groups
++;
219 bank
->chips
[group
- bank
->start
] = chip
;
220 for (i
= 0; i
< chip
->chip
.ngpio
; i
++) {
221 irq
= chip
->irq_base
+ i
;
222 set_irq_chip(irq
, &s5p_gpioint
);
223 set_irq_data(irq
, chip
);
224 set_irq_handler(irq
, handle_level_irq
);
225 set_irq_flags(irq
, IRQF_VALID
);
230 int __init
s5p_register_gpio_interrupt(int pin
)
232 struct s3c_gpio_chip
*my_chip
= s3c_gpiolib_getchip(pin
);
239 offset
= pin
- my_chip
->chip
.base
;
240 group
= my_chip
->group
;
242 /* check if the group has been already registered */
243 if (my_chip
->irq_base
)
244 return my_chip
->irq_base
+ offset
;
246 /* register gpio group */
247 ret
= s5p_gpioint_add(my_chip
);
249 my_chip
->chip
.to_irq
= samsung_gpiolib_to_irq
;
250 printk(KERN_INFO
"Registered interrupt support for gpio group %d.\n",
252 return my_chip
->irq_base
+ offset
;
257 int __init
s5p_register_gpioint_bank(int chain_irq
, int start
, int nr_groups
)
259 struct s5p_gpioint_bank
*bank
;
261 bank
= kzalloc(sizeof(*bank
), GFP_KERNEL
);
266 bank
->nr_groups
= nr_groups
;
267 bank
->irq
= chain_irq
;
269 list_add_tail(&bank
->list
, &banks
);