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 #include <asm/mach/irq.h>
28 #define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u)
30 #define CON_OFFSET 0x700
31 #define MASK_OFFSET 0x900
32 #define PEND_OFFSET 0xA00
33 #define REG_OFFSET(x) ((x) << 2)
35 struct s5p_gpioint_bank
{
36 struct list_head list
;
40 struct samsung_gpio_chip
**chips
;
41 void (*handler
)(unsigned int, struct irq_desc
*);
46 static int s5p_gpioint_set_type(struct irq_data
*d
, unsigned int type
)
48 struct irq_chip_generic
*gc
= irq_data_get_irq_chip_data(d
);
49 struct irq_chip_type
*ct
= gc
->chip_types
;
50 unsigned int shift
= (d
->irq
- gc
->irq_base
) << 2;
53 case IRQ_TYPE_EDGE_RISING
:
54 type
= S5P_IRQ_TYPE_EDGE_RISING
;
56 case IRQ_TYPE_EDGE_FALLING
:
57 type
= S5P_IRQ_TYPE_EDGE_FALLING
;
59 case IRQ_TYPE_EDGE_BOTH
:
60 type
= S5P_IRQ_TYPE_EDGE_BOTH
;
62 case IRQ_TYPE_LEVEL_HIGH
:
63 type
= S5P_IRQ_TYPE_LEVEL_HIGH
;
65 case IRQ_TYPE_LEVEL_LOW
:
66 type
= S5P_IRQ_TYPE_LEVEL_LOW
;
70 printk(KERN_WARNING
"No irq type\n");
74 gc
->type_cache
&= ~(0x7 << shift
);
75 gc
->type_cache
|= type
<< shift
;
76 writel(gc
->type_cache
, gc
->reg_base
+ ct
->regs
.type
);
80 static void s5p_gpioint_handler(unsigned int irq
, struct irq_desc
*desc
)
82 struct s5p_gpioint_bank
*bank
= irq_get_handler_data(irq
);
83 int group
, pend_offset
, mask_offset
;
84 unsigned int pend
, mask
;
86 struct irq_chip
*chip
= irq_get_chip(irq
);
87 chained_irq_enter(chip
, desc
);
89 for (group
= 0; group
< bank
->nr_groups
; group
++) {
90 struct samsung_gpio_chip
*chip
= bank
->chips
[group
];
94 pend_offset
= REG_OFFSET(group
);
95 pend
= __raw_readl(GPIO_BASE(chip
) + PEND_OFFSET
+ pend_offset
);
99 mask_offset
= REG_OFFSET(group
);
100 mask
= __raw_readl(GPIO_BASE(chip
) + MASK_OFFSET
+ mask_offset
);
104 int offset
= fls(pend
) - 1;
105 int real_irq
= chip
->irq_base
+ offset
;
106 generic_handle_irq(real_irq
);
107 pend
&= ~BIT(offset
);
110 chained_irq_exit(chip
, desc
);
113 static __init
int s5p_gpioint_add(struct samsung_gpio_chip
*chip
)
115 static int used_gpioint_groups
= 0;
116 int group
= chip
->group
;
117 struct s5p_gpioint_bank
*b
, *bank
= NULL
;
118 struct irq_chip_generic
*gc
;
119 struct irq_chip_type
*ct
;
121 if (used_gpioint_groups
>= S5P_GPIOINT_GROUP_COUNT
)
124 list_for_each_entry(b
, &banks
, list
) {
125 if (group
>= b
->start
&& group
< b
->start
+ b
->nr_groups
) {
133 if (!bank
->handler
) {
134 bank
->chips
= kzalloc(sizeof(struct samsung_gpio_chip
*) *
135 bank
->nr_groups
, GFP_KERNEL
);
139 irq_set_chained_handler(bank
->irq
, s5p_gpioint_handler
);
140 irq_set_handler_data(bank
->irq
, bank
);
141 bank
->handler
= s5p_gpioint_handler
;
142 printk(KERN_INFO
"Registered chained gpio int handler for interrupt %d.\n",
147 * chained GPIO irq has been successfully registered, allocate new gpio
148 * int group and assign irq nubmers
150 chip
->irq_base
= S5P_GPIOINT_BASE
+
151 used_gpioint_groups
* S5P_GPIOINT_GROUP_SIZE
;
152 used_gpioint_groups
++;
154 bank
->chips
[group
- bank
->start
] = chip
;
156 gc
= irq_alloc_generic_chip("s5p_gpioint", 1, chip
->irq_base
,
157 (void __iomem
*)GPIO_BASE(chip
),
162 ct
->chip
.irq_ack
= irq_gc_ack_set_bit
;
163 ct
->chip
.irq_mask
= irq_gc_mask_set_bit
;
164 ct
->chip
.irq_unmask
= irq_gc_mask_clr_bit
;
165 ct
->chip
.irq_set_type
= s5p_gpioint_set_type
,
166 ct
->regs
.ack
= PEND_OFFSET
+ REG_OFFSET(group
- bank
->start
);
167 ct
->regs
.mask
= MASK_OFFSET
+ REG_OFFSET(group
- bank
->start
);
168 ct
->regs
.type
= CON_OFFSET
+ REG_OFFSET(group
- bank
->start
);
169 irq_setup_generic_chip(gc
, IRQ_MSK(chip
->chip
.ngpio
),
170 IRQ_GC_INIT_MASK_CACHE
,
171 IRQ_NOREQUEST
| IRQ_NOPROBE
, 0);
175 int __init
s5p_register_gpio_interrupt(int pin
)
177 struct samsung_gpio_chip
*my_chip
= samsung_gpiolib_getchip(pin
);
184 offset
= pin
- my_chip
->chip
.base
;
185 group
= my_chip
->group
;
187 /* check if the group has been already registered */
188 if (my_chip
->irq_base
)
189 return my_chip
->irq_base
+ offset
;
191 /* register gpio group */
192 ret
= s5p_gpioint_add(my_chip
);
194 my_chip
->chip
.to_irq
= samsung_gpiolib_to_irq
;
195 printk(KERN_INFO
"Registered interrupt support for gpio group %d.\n",
197 return my_chip
->irq_base
+ offset
;
202 int __init
s5p_register_gpioint_bank(int chain_irq
, int start
, int nr_groups
)
204 struct s5p_gpioint_bank
*bank
;
206 bank
= kzalloc(sizeof(*bank
), GFP_KERNEL
);
211 bank
->nr_groups
= nr_groups
;
212 bank
->irq
= chain_irq
;
214 list_add_tail(&bank
->list
, &banks
);