2 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
3 * Author: Kyungmin Park <kyungmin.park@samsung.com>
4 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
5 * Author: Marek Szyprowski <m.szyprowski@samsung.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
14 #include <linux/kernel.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
18 #include <linux/gpio.h>
19 #include <linux/slab.h>
22 #include <plat/gpio-core.h>
23 #include <plat/gpio-cfg.h>
25 #include <asm/mach/irq.h>
27 #define GPIO_BASE(chip) ((void __iomem *)((unsigned long)((chip)->base) & 0xFFFFF000u))
29 #define CON_OFFSET 0x700
30 #define MASK_OFFSET 0x900
31 #define PEND_OFFSET 0xA00
32 #define REG_OFFSET(x) ((x) << 2)
34 struct s5p_gpioint_bank
{
35 struct list_head list
;
39 struct samsung_gpio_chip
**chips
;
40 void (*handler
)(unsigned int, struct irq_desc
*);
43 static LIST_HEAD(banks
);
45 static int s5p_gpioint_set_type(struct irq_data
*d
, unsigned int type
)
47 struct irq_chip_generic
*gc
= irq_data_get_irq_chip_data(d
);
48 struct irq_chip_type
*ct
= gc
->chip_types
;
49 unsigned int shift
= (d
->irq
- gc
->irq_base
) << 2;
52 case IRQ_TYPE_EDGE_RISING
:
53 type
= S5P_IRQ_TYPE_EDGE_RISING
;
55 case IRQ_TYPE_EDGE_FALLING
:
56 type
= S5P_IRQ_TYPE_EDGE_FALLING
;
58 case IRQ_TYPE_EDGE_BOTH
:
59 type
= S5P_IRQ_TYPE_EDGE_BOTH
;
61 case IRQ_TYPE_LEVEL_HIGH
:
62 type
= S5P_IRQ_TYPE_LEVEL_HIGH
;
64 case IRQ_TYPE_LEVEL_LOW
:
65 type
= S5P_IRQ_TYPE_LEVEL_LOW
;
69 printk(KERN_WARNING
"No irq type\n");
73 gc
->type_cache
&= ~(0x7 << shift
);
74 gc
->type_cache
|= type
<< shift
;
75 writel(gc
->type_cache
, gc
->reg_base
+ ct
->regs
.type
);
79 static void s5p_gpioint_handler(unsigned int irq
, struct irq_desc
*desc
)
81 struct s5p_gpioint_bank
*bank
= irq_get_handler_data(irq
);
82 int group
, pend_offset
, mask_offset
;
83 unsigned int pend
, mask
;
85 struct irq_chip
*chip
= irq_get_chip(irq
);
86 chained_irq_enter(chip
, desc
);
88 for (group
= 0; group
< bank
->nr_groups
; group
++) {
89 struct samsung_gpio_chip
*chip
= bank
->chips
[group
];
93 pend_offset
= REG_OFFSET(group
);
94 pend
= __raw_readl(GPIO_BASE(chip
) + PEND_OFFSET
+ pend_offset
);
98 mask_offset
= REG_OFFSET(group
);
99 mask
= __raw_readl(GPIO_BASE(chip
) + MASK_OFFSET
+ mask_offset
);
103 int offset
= fls(pend
) - 1;
104 int real_irq
= chip
->irq_base
+ offset
;
105 generic_handle_irq(real_irq
);
106 pend
&= ~BIT(offset
);
109 chained_irq_exit(chip
, desc
);
112 static __init
int s5p_gpioint_add(struct samsung_gpio_chip
*chip
)
114 static int used_gpioint_groups
= 0;
115 int group
= chip
->group
;
116 struct s5p_gpioint_bank
*b
, *bank
= NULL
;
117 struct irq_chip_generic
*gc
;
118 struct irq_chip_type
*ct
;
120 if (used_gpioint_groups
>= S5P_GPIOINT_GROUP_COUNT
)
123 list_for_each_entry(b
, &banks
, list
) {
124 if (group
>= b
->start
&& group
< b
->start
+ b
->nr_groups
) {
132 if (!bank
->handler
) {
133 bank
->chips
= kzalloc(sizeof(struct samsung_gpio_chip
*) *
134 bank
->nr_groups
, GFP_KERNEL
);
138 irq_set_chained_handler(bank
->irq
, s5p_gpioint_handler
);
139 irq_set_handler_data(bank
->irq
, bank
);
140 bank
->handler
= s5p_gpioint_handler
;
141 printk(KERN_INFO
"Registered chained gpio int handler for interrupt %d.\n",
146 * chained GPIO irq has been successfully registered, allocate new gpio
147 * int group and assign irq nubmers
149 chip
->irq_base
= S5P_GPIOINT_BASE
+
150 used_gpioint_groups
* S5P_GPIOINT_GROUP_SIZE
;
151 used_gpioint_groups
++;
153 bank
->chips
[group
- bank
->start
] = chip
;
155 gc
= irq_alloc_generic_chip("s5p_gpioint", 1, chip
->irq_base
,
161 ct
->chip
.irq_ack
= irq_gc_ack_set_bit
;
162 ct
->chip
.irq_mask
= irq_gc_mask_set_bit
;
163 ct
->chip
.irq_unmask
= irq_gc_mask_clr_bit
;
164 ct
->chip
.irq_set_type
= s5p_gpioint_set_type
,
165 ct
->regs
.ack
= PEND_OFFSET
+ REG_OFFSET(group
- bank
->start
);
166 ct
->regs
.mask
= MASK_OFFSET
+ REG_OFFSET(group
- bank
->start
);
167 ct
->regs
.type
= CON_OFFSET
+ REG_OFFSET(group
- bank
->start
);
168 irq_setup_generic_chip(gc
, IRQ_MSK(chip
->chip
.ngpio
),
169 IRQ_GC_INIT_MASK_CACHE
,
170 IRQ_NOREQUEST
| IRQ_NOPROBE
, 0);
174 int __init
s5p_register_gpio_interrupt(int pin
)
176 struct samsung_gpio_chip
*my_chip
= samsung_gpiolib_getchip(pin
);
183 offset
= pin
- my_chip
->chip
.base
;
184 group
= my_chip
->group
;
186 /* check if the group has been already registered */
187 if (my_chip
->irq_base
)
190 /* register gpio group */
191 ret
= s5p_gpioint_add(my_chip
);
193 my_chip
->chip
.to_irq
= samsung_gpiolib_to_irq
;
194 printk(KERN_INFO
"Registered interrupt support for gpio group %d.\n",
200 my_chip
->bitmap_gpio_int
|= BIT(offset
);
202 return my_chip
->irq_base
+ offset
;
205 int __init
s5p_register_gpioint_bank(int chain_irq
, int start
, int nr_groups
)
207 struct s5p_gpioint_bank
*bank
;
209 bank
= kzalloc(sizeof(*bank
), GFP_KERNEL
);
214 bank
->nr_groups
= nr_groups
;
215 bank
->irq
= chain_irq
;
217 list_add_tail(&bank
->list
, &banks
);