Commit 68ae89984710d2e9f3cf2005539c8f91bcce9d40
1 parent
bf46aaeacf
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
ARM: SAMSUNG: move interrupt part for common s5p into plat-samsung
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Showing 10 changed files with 487 additions and 485 deletions Side-by-side Diff
- arch/arm/plat-s5p/Kconfig
- arch/arm/plat-s5p/Makefile
- arch/arm/plat-s5p/irq-eint.c
- arch/arm/plat-s5p/irq-gpioint.c
- arch/arm/plat-s5p/irq.c
- arch/arm/plat-samsung/Kconfig
- arch/arm/plat-samsung/Makefile
- arch/arm/plat-samsung/s5p-irq-eint.c
- arch/arm/plat-samsung/s5p-irq-gpioint.c
- arch/arm/plat-samsung/s5p-irq.c
arch/arm/plat-s5p/Kconfig
... | ... | @@ -23,17 +23,6 @@ |
23 | 23 | help |
24 | 24 | Base platform code for Samsung's S5P series SoC. |
25 | 25 | |
26 | -config S5P_EXT_INT | |
27 | - bool | |
28 | - help | |
29 | - Use the external interrupts (other than GPIO interrupts.) | |
30 | - Note: Do not choose this for S5P6440 and S5P6450. | |
31 | - | |
32 | -config S5P_GPIO_INT | |
33 | - bool | |
34 | - help | |
35 | - Common code for the GPIO interrupts (other than external interrupts.) | |
36 | - | |
37 | 26 | config S5P_HRT |
38 | 27 | bool |
39 | 28 | select SAMSUNG_DEV_PWM |
arch/arm/plat-s5p/Makefile
arch/arm/plat-s5p/irq-eint.c
1 | -/* linux/arch/arm/plat-s5p/irq-eint.c | |
2 | - * | |
3 | - * Copyright (c) 2010 Samsung Electronics Co., Ltd. | |
4 | - * http://www.samsung.com | |
5 | - * | |
6 | - * S5P - IRQ EINT support | |
7 | - * | |
8 | - * This program is free software; you can redistribute it and/or modify | |
9 | - * it under the terms of the GNU General Public License version 2 as | |
10 | - * published by the Free Software Foundation. | |
11 | -*/ | |
12 | - | |
13 | -#include <linux/kernel.h> | |
14 | -#include <linux/interrupt.h> | |
15 | -#include <linux/irq.h> | |
16 | -#include <linux/io.h> | |
17 | -#include <linux/device.h> | |
18 | -#include <linux/gpio.h> | |
19 | - | |
20 | -#include <asm/hardware/vic.h> | |
21 | - | |
22 | -#include <plat/regs-irqtype.h> | |
23 | - | |
24 | -#include <mach/map.h> | |
25 | -#include <plat/cpu.h> | |
26 | -#include <plat/pm.h> | |
27 | - | |
28 | -#include <plat/gpio-cfg.h> | |
29 | -#include <mach/regs-gpio.h> | |
30 | - | |
31 | -static inline void s5p_irq_eint_mask(struct irq_data *data) | |
32 | -{ | |
33 | - u32 mask; | |
34 | - | |
35 | - mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
36 | - mask |= eint_irq_to_bit(data->irq); | |
37 | - __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
38 | -} | |
39 | - | |
40 | -static void s5p_irq_eint_unmask(struct irq_data *data) | |
41 | -{ | |
42 | - u32 mask; | |
43 | - | |
44 | - mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
45 | - mask &= ~(eint_irq_to_bit(data->irq)); | |
46 | - __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
47 | -} | |
48 | - | |
49 | -static inline void s5p_irq_eint_ack(struct irq_data *data) | |
50 | -{ | |
51 | - __raw_writel(eint_irq_to_bit(data->irq), | |
52 | - S5P_EINT_PEND(EINT_REG_NR(data->irq))); | |
53 | -} | |
54 | - | |
55 | -static void s5p_irq_eint_maskack(struct irq_data *data) | |
56 | -{ | |
57 | - /* compiler should in-line these */ | |
58 | - s5p_irq_eint_mask(data); | |
59 | - s5p_irq_eint_ack(data); | |
60 | -} | |
61 | - | |
62 | -static int s5p_irq_eint_set_type(struct irq_data *data, unsigned int type) | |
63 | -{ | |
64 | - int offs = EINT_OFFSET(data->irq); | |
65 | - int shift; | |
66 | - u32 ctrl, mask; | |
67 | - u32 newvalue = 0; | |
68 | - | |
69 | - switch (type) { | |
70 | - case IRQ_TYPE_EDGE_RISING: | |
71 | - newvalue = S5P_IRQ_TYPE_EDGE_RISING; | |
72 | - break; | |
73 | - | |
74 | - case IRQ_TYPE_EDGE_FALLING: | |
75 | - newvalue = S5P_IRQ_TYPE_EDGE_FALLING; | |
76 | - break; | |
77 | - | |
78 | - case IRQ_TYPE_EDGE_BOTH: | |
79 | - newvalue = S5P_IRQ_TYPE_EDGE_BOTH; | |
80 | - break; | |
81 | - | |
82 | - case IRQ_TYPE_LEVEL_LOW: | |
83 | - newvalue = S5P_IRQ_TYPE_LEVEL_LOW; | |
84 | - break; | |
85 | - | |
86 | - case IRQ_TYPE_LEVEL_HIGH: | |
87 | - newvalue = S5P_IRQ_TYPE_LEVEL_HIGH; | |
88 | - break; | |
89 | - | |
90 | - default: | |
91 | - printk(KERN_ERR "No such irq type %d", type); | |
92 | - return -EINVAL; | |
93 | - } | |
94 | - | |
95 | - shift = (offs & 0x7) * 4; | |
96 | - mask = 0x7 << shift; | |
97 | - | |
98 | - ctrl = __raw_readl(S5P_EINT_CON(EINT_REG_NR(data->irq))); | |
99 | - ctrl &= ~mask; | |
100 | - ctrl |= newvalue << shift; | |
101 | - __raw_writel(ctrl, S5P_EINT_CON(EINT_REG_NR(data->irq))); | |
102 | - | |
103 | - if ((0 <= offs) && (offs < 8)) | |
104 | - s3c_gpio_cfgpin(EINT_GPIO_0(offs & 0x7), EINT_MODE); | |
105 | - | |
106 | - else if ((8 <= offs) && (offs < 16)) | |
107 | - s3c_gpio_cfgpin(EINT_GPIO_1(offs & 0x7), EINT_MODE); | |
108 | - | |
109 | - else if ((16 <= offs) && (offs < 24)) | |
110 | - s3c_gpio_cfgpin(EINT_GPIO_2(offs & 0x7), EINT_MODE); | |
111 | - | |
112 | - else if ((24 <= offs) && (offs < 32)) | |
113 | - s3c_gpio_cfgpin(EINT_GPIO_3(offs & 0x7), EINT_MODE); | |
114 | - | |
115 | - else | |
116 | - printk(KERN_ERR "No such irq number %d", offs); | |
117 | - | |
118 | - return 0; | |
119 | -} | |
120 | - | |
121 | -static struct irq_chip s5p_irq_eint = { | |
122 | - .name = "s5p-eint", | |
123 | - .irq_mask = s5p_irq_eint_mask, | |
124 | - .irq_unmask = s5p_irq_eint_unmask, | |
125 | - .irq_mask_ack = s5p_irq_eint_maskack, | |
126 | - .irq_ack = s5p_irq_eint_ack, | |
127 | - .irq_set_type = s5p_irq_eint_set_type, | |
128 | -#ifdef CONFIG_PM | |
129 | - .irq_set_wake = s3c_irqext_wake, | |
130 | -#endif | |
131 | -}; | |
132 | - | |
133 | -/* s5p_irq_demux_eint | |
134 | - * | |
135 | - * This function demuxes the IRQ from the group0 external interrupts, | |
136 | - * from EINTs 16 to 31. It is designed to be inlined into the specific | |
137 | - * handler s5p_irq_demux_eintX_Y. | |
138 | - * | |
139 | - * Each EINT pend/mask registers handle eight of them. | |
140 | - */ | |
141 | -static inline void s5p_irq_demux_eint(unsigned int start) | |
142 | -{ | |
143 | - u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start))); | |
144 | - u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start))); | |
145 | - unsigned int irq; | |
146 | - | |
147 | - status &= ~mask; | |
148 | - status &= 0xff; | |
149 | - | |
150 | - while (status) { | |
151 | - irq = fls(status) - 1; | |
152 | - generic_handle_irq(irq + start); | |
153 | - status &= ~(1 << irq); | |
154 | - } | |
155 | -} | |
156 | - | |
157 | -static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) | |
158 | -{ | |
159 | - s5p_irq_demux_eint(IRQ_EINT(16)); | |
160 | - s5p_irq_demux_eint(IRQ_EINT(24)); | |
161 | -} | |
162 | - | |
163 | -static inline void s5p_irq_vic_eint_mask(struct irq_data *data) | |
164 | -{ | |
165 | - void __iomem *base = irq_data_get_irq_chip_data(data); | |
166 | - | |
167 | - s5p_irq_eint_mask(data); | |
168 | - writel(1 << EINT_OFFSET(data->irq), base + VIC_INT_ENABLE_CLEAR); | |
169 | -} | |
170 | - | |
171 | -static void s5p_irq_vic_eint_unmask(struct irq_data *data) | |
172 | -{ | |
173 | - void __iomem *base = irq_data_get_irq_chip_data(data); | |
174 | - | |
175 | - s5p_irq_eint_unmask(data); | |
176 | - writel(1 << EINT_OFFSET(data->irq), base + VIC_INT_ENABLE); | |
177 | -} | |
178 | - | |
179 | -static inline void s5p_irq_vic_eint_ack(struct irq_data *data) | |
180 | -{ | |
181 | - __raw_writel(eint_irq_to_bit(data->irq), | |
182 | - S5P_EINT_PEND(EINT_REG_NR(data->irq))); | |
183 | -} | |
184 | - | |
185 | -static void s5p_irq_vic_eint_maskack(struct irq_data *data) | |
186 | -{ | |
187 | - s5p_irq_vic_eint_mask(data); | |
188 | - s5p_irq_vic_eint_ack(data); | |
189 | -} | |
190 | - | |
191 | -static struct irq_chip s5p_irq_vic_eint = { | |
192 | - .name = "s5p_vic_eint", | |
193 | - .irq_mask = s5p_irq_vic_eint_mask, | |
194 | - .irq_unmask = s5p_irq_vic_eint_unmask, | |
195 | - .irq_mask_ack = s5p_irq_vic_eint_maskack, | |
196 | - .irq_ack = s5p_irq_vic_eint_ack, | |
197 | - .irq_set_type = s5p_irq_eint_set_type, | |
198 | -#ifdef CONFIG_PM | |
199 | - .irq_set_wake = s3c_irqext_wake, | |
200 | -#endif | |
201 | -}; | |
202 | - | |
203 | -static int __init s5p_init_irq_eint(void) | |
204 | -{ | |
205 | - int irq; | |
206 | - | |
207 | - for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++) | |
208 | - irq_set_chip(irq, &s5p_irq_vic_eint); | |
209 | - | |
210 | - for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) { | |
211 | - irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq); | |
212 | - set_irq_flags(irq, IRQF_VALID); | |
213 | - } | |
214 | - | |
215 | - irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31); | |
216 | - return 0; | |
217 | -} | |
218 | - | |
219 | -arch_initcall(s5p_init_irq_eint); |
arch/arm/plat-s5p/irq-gpioint.c
1 | -/* linux/arch/arm/plat-s5p/irq-gpioint.c | |
2 | - * | |
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> | |
7 | - * | |
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. | |
12 | - * | |
13 | - */ | |
14 | - | |
15 | -#include <linux/kernel.h> | |
16 | -#include <linux/interrupt.h> | |
17 | -#include <linux/irq.h> | |
18 | -#include <linux/io.h> | |
19 | -#include <linux/gpio.h> | |
20 | -#include <linux/slab.h> | |
21 | - | |
22 | -#include <mach/map.h> | |
23 | -#include <plat/gpio-core.h> | |
24 | -#include <plat/gpio-cfg.h> | |
25 | - | |
26 | -#include <asm/mach/irq.h> | |
27 | - | |
28 | -#define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u) | |
29 | - | |
30 | -#define CON_OFFSET 0x700 | |
31 | -#define MASK_OFFSET 0x900 | |
32 | -#define PEND_OFFSET 0xA00 | |
33 | -#define REG_OFFSET(x) ((x) << 2) | |
34 | - | |
35 | -struct s5p_gpioint_bank { | |
36 | - struct list_head list; | |
37 | - int start; | |
38 | - int nr_groups; | |
39 | - int irq; | |
40 | - struct samsung_gpio_chip **chips; | |
41 | - void (*handler)(unsigned int, struct irq_desc *); | |
42 | -}; | |
43 | - | |
44 | -static LIST_HEAD(banks); | |
45 | - | |
46 | -static int s5p_gpioint_set_type(struct irq_data *d, unsigned int type) | |
47 | -{ | |
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; | |
51 | - | |
52 | - switch (type) { | |
53 | - case IRQ_TYPE_EDGE_RISING: | |
54 | - type = S5P_IRQ_TYPE_EDGE_RISING; | |
55 | - break; | |
56 | - case IRQ_TYPE_EDGE_FALLING: | |
57 | - type = S5P_IRQ_TYPE_EDGE_FALLING; | |
58 | - break; | |
59 | - case IRQ_TYPE_EDGE_BOTH: | |
60 | - type = S5P_IRQ_TYPE_EDGE_BOTH; | |
61 | - break; | |
62 | - case IRQ_TYPE_LEVEL_HIGH: | |
63 | - type = S5P_IRQ_TYPE_LEVEL_HIGH; | |
64 | - break; | |
65 | - case IRQ_TYPE_LEVEL_LOW: | |
66 | - type = S5P_IRQ_TYPE_LEVEL_LOW; | |
67 | - break; | |
68 | - case IRQ_TYPE_NONE: | |
69 | - default: | |
70 | - printk(KERN_WARNING "No irq type\n"); | |
71 | - return -EINVAL; | |
72 | - } | |
73 | - | |
74 | - gc->type_cache &= ~(0x7 << shift); | |
75 | - gc->type_cache |= type << shift; | |
76 | - writel(gc->type_cache, gc->reg_base + ct->regs.type); | |
77 | - return 0; | |
78 | -} | |
79 | - | |
80 | -static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) | |
81 | -{ | |
82 | - struct s5p_gpioint_bank *bank = irq_get_handler_data(irq); | |
83 | - int group, pend_offset, mask_offset; | |
84 | - unsigned int pend, mask; | |
85 | - | |
86 | - struct irq_chip *chip = irq_get_chip(irq); | |
87 | - chained_irq_enter(chip, desc); | |
88 | - | |
89 | - for (group = 0; group < bank->nr_groups; group++) { | |
90 | - struct samsung_gpio_chip *chip = bank->chips[group]; | |
91 | - if (!chip) | |
92 | - continue; | |
93 | - | |
94 | - pend_offset = REG_OFFSET(group); | |
95 | - pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset); | |
96 | - if (!pend) | |
97 | - continue; | |
98 | - | |
99 | - mask_offset = REG_OFFSET(group); | |
100 | - mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); | |
101 | - pend &= ~mask; | |
102 | - | |
103 | - while (pend) { | |
104 | - int offset = fls(pend) - 1; | |
105 | - int real_irq = chip->irq_base + offset; | |
106 | - generic_handle_irq(real_irq); | |
107 | - pend &= ~BIT(offset); | |
108 | - } | |
109 | - } | |
110 | - chained_irq_exit(chip, desc); | |
111 | -} | |
112 | - | |
113 | -static __init int s5p_gpioint_add(struct samsung_gpio_chip *chip) | |
114 | -{ | |
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; | |
120 | - | |
121 | - if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) | |
122 | - return -ENOMEM; | |
123 | - | |
124 | - list_for_each_entry(b, &banks, list) { | |
125 | - if (group >= b->start && group < b->start + b->nr_groups) { | |
126 | - bank = b; | |
127 | - break; | |
128 | - } | |
129 | - } | |
130 | - if (!bank) | |
131 | - return -EINVAL; | |
132 | - | |
133 | - if (!bank->handler) { | |
134 | - bank->chips = kzalloc(sizeof(struct samsung_gpio_chip *) * | |
135 | - bank->nr_groups, GFP_KERNEL); | |
136 | - if (!bank->chips) | |
137 | - return -ENOMEM; | |
138 | - | |
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", | |
143 | - bank->irq); | |
144 | - } | |
145 | - | |
146 | - /* | |
147 | - * chained GPIO irq has been successfully registered, allocate new gpio | |
148 | - * int group and assign irq nubmers | |
149 | - */ | |
150 | - chip->irq_base = S5P_GPIOINT_BASE + | |
151 | - used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE; | |
152 | - used_gpioint_groups++; | |
153 | - | |
154 | - bank->chips[group - bank->start] = chip; | |
155 | - | |
156 | - gc = irq_alloc_generic_chip("s5p_gpioint", 1, chip->irq_base, | |
157 | - (void __iomem *)GPIO_BASE(chip), | |
158 | - handle_level_irq); | |
159 | - if (!gc) | |
160 | - return -ENOMEM; | |
161 | - ct = gc->chip_types; | |
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); | |
172 | - return 0; | |
173 | -} | |
174 | - | |
175 | -int __init s5p_register_gpio_interrupt(int pin) | |
176 | -{ | |
177 | - struct samsung_gpio_chip *my_chip = samsung_gpiolib_getchip(pin); | |
178 | - int offset, group; | |
179 | - int ret; | |
180 | - | |
181 | - if (!my_chip) | |
182 | - return -EINVAL; | |
183 | - | |
184 | - offset = pin - my_chip->chip.base; | |
185 | - group = my_chip->group; | |
186 | - | |
187 | - /* check if the group has been already registered */ | |
188 | - if (my_chip->irq_base) | |
189 | - return my_chip->irq_base + offset; | |
190 | - | |
191 | - /* register gpio group */ | |
192 | - ret = s5p_gpioint_add(my_chip); | |
193 | - if (ret == 0) { | |
194 | - my_chip->chip.to_irq = samsung_gpiolib_to_irq; | |
195 | - printk(KERN_INFO "Registered interrupt support for gpio group %d.\n", | |
196 | - group); | |
197 | - return my_chip->irq_base + offset; | |
198 | - } | |
199 | - return ret; | |
200 | -} | |
201 | - | |
202 | -int __init s5p_register_gpioint_bank(int chain_irq, int start, int nr_groups) | |
203 | -{ | |
204 | - struct s5p_gpioint_bank *bank; | |
205 | - | |
206 | - bank = kzalloc(sizeof(*bank), GFP_KERNEL); | |
207 | - if (!bank) | |
208 | - return -ENOMEM; | |
209 | - | |
210 | - bank->start = start; | |
211 | - bank->nr_groups = nr_groups; | |
212 | - bank->irq = chain_irq; | |
213 | - | |
214 | - list_add_tail(&bank->list, &banks); | |
215 | - return 0; | |
216 | -} |
arch/arm/plat-s5p/irq.c
1 | -/* arch/arm/plat-s5p/irq.c | |
2 | - * | |
3 | - * Copyright (c) 2009 Samsung Electronics Co., Ltd. | |
4 | - * http://www.samsung.com/ | |
5 | - * | |
6 | - * S5P - Interrupt handling | |
7 | - * | |
8 | - * This program is free software; you can redistribute it and/or modify | |
9 | - * it under the terms of the GNU General Public License version 2 as | |
10 | - * published by the Free Software Foundation. | |
11 | -*/ | |
12 | - | |
13 | -#include <linux/kernel.h> | |
14 | -#include <linux/interrupt.h> | |
15 | -#include <linux/irq.h> | |
16 | -#include <linux/io.h> | |
17 | - | |
18 | -#include <asm/hardware/vic.h> | |
19 | - | |
20 | -#include <mach/map.h> | |
21 | -#include <plat/regs-timer.h> | |
22 | -#include <plat/cpu.h> | |
23 | -#include <plat/irq-vic-timer.h> | |
24 | - | |
25 | -void __init s5p_init_irq(u32 *vic, u32 num_vic) | |
26 | -{ | |
27 | -#ifdef CONFIG_ARM_VIC | |
28 | - int irq; | |
29 | - | |
30 | - /* initialize the VICs */ | |
31 | - for (irq = 0; irq < num_vic; irq++) | |
32 | - vic_init(VA_VIC(irq), VIC_BASE(irq), vic[irq], 0); | |
33 | -#endif | |
34 | - | |
35 | - s3c_init_vic_timer_irq(5, IRQ_TIMER0); | |
36 | -} |
arch/arm/plat-samsung/Kconfig
... | ... | @@ -70,6 +70,22 @@ |
70 | 70 | help |
71 | 71 | Internal configuration to build the VIC timer interrupt code. |
72 | 72 | |
73 | +config S5P_IRQ | |
74 | + def_bool (ARCH_S5P64X0 || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS) | |
75 | + help | |
76 | + Support common interrup part for ARCH_S5P and ARCH_EXYNOS SoCs | |
77 | + | |
78 | +config S5P_EXT_INT | |
79 | + bool | |
80 | + help | |
81 | + Use the external interrupts (other than GPIO interrupts.) | |
82 | + Note: Do not choose this for S5P6440 and S5P6450. | |
83 | + | |
84 | +config S5P_GPIO_INT | |
85 | + bool | |
86 | + help | |
87 | + Common code for the GPIO interrupts (other than external interrupts.) | |
88 | + | |
73 | 89 | # options for gpio configuration support |
74 | 90 | |
75 | 91 | config SAMSUNG_GPIOLIB_4BIT |
arch/arm/plat-samsung/Makefile
... | ... | @@ -20,6 +20,9 @@ |
20 | 20 | obj-$(CONFIG_S5P_CLOCK) += s5p-clock.o |
21 | 21 | |
22 | 22 | obj-$(CONFIG_SAMSUNG_IRQ_VIC_TIMER) += irq-vic-timer.o |
23 | +obj-$(CONFIG_S5P_IRQ) += s5p-irq.o | |
24 | +obj-$(CONFIG_S5P_EXT_INT) += s5p-irq-eint.o | |
25 | +obj-$(CONFIG_S5P_GPIO_INT) += s5p-irq-gpioint.o | |
23 | 26 | |
24 | 27 | # ADC |
25 | 28 |
arch/arm/plat-samsung/s5p-irq-eint.c
1 | +/* | |
2 | + * Copyright (c) 2010 Samsung Electronics Co., Ltd. | |
3 | + * http://www.samsung.com | |
4 | + * | |
5 | + * S5P - IRQ EINT support | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify | |
8 | + * it under the terms of the GNU General Public License version 2 as | |
9 | + * published by the Free Software Foundation. | |
10 | +*/ | |
11 | + | |
12 | +#include <linux/kernel.h> | |
13 | +#include <linux/interrupt.h> | |
14 | +#include <linux/irq.h> | |
15 | +#include <linux/io.h> | |
16 | +#include <linux/device.h> | |
17 | +#include <linux/gpio.h> | |
18 | + | |
19 | +#include <asm/hardware/vic.h> | |
20 | + | |
21 | +#include <plat/regs-irqtype.h> | |
22 | + | |
23 | +#include <mach/map.h> | |
24 | +#include <plat/cpu.h> | |
25 | +#include <plat/pm.h> | |
26 | + | |
27 | +#include <plat/gpio-cfg.h> | |
28 | +#include <mach/regs-gpio.h> | |
29 | + | |
30 | +static inline void s5p_irq_eint_mask(struct irq_data *data) | |
31 | +{ | |
32 | + u32 mask; | |
33 | + | |
34 | + mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
35 | + mask |= eint_irq_to_bit(data->irq); | |
36 | + __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
37 | +} | |
38 | + | |
39 | +static void s5p_irq_eint_unmask(struct irq_data *data) | |
40 | +{ | |
41 | + u32 mask; | |
42 | + | |
43 | + mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
44 | + mask &= ~(eint_irq_to_bit(data->irq)); | |
45 | + __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq))); | |
46 | +} | |
47 | + | |
48 | +static inline void s5p_irq_eint_ack(struct irq_data *data) | |
49 | +{ | |
50 | + __raw_writel(eint_irq_to_bit(data->irq), | |
51 | + S5P_EINT_PEND(EINT_REG_NR(data->irq))); | |
52 | +} | |
53 | + | |
54 | +static void s5p_irq_eint_maskack(struct irq_data *data) | |
55 | +{ | |
56 | + /* compiler should in-line these */ | |
57 | + s5p_irq_eint_mask(data); | |
58 | + s5p_irq_eint_ack(data); | |
59 | +} | |
60 | + | |
61 | +static int s5p_irq_eint_set_type(struct irq_data *data, unsigned int type) | |
62 | +{ | |
63 | + int offs = EINT_OFFSET(data->irq); | |
64 | + int shift; | |
65 | + u32 ctrl, mask; | |
66 | + u32 newvalue = 0; | |
67 | + | |
68 | + switch (type) { | |
69 | + case IRQ_TYPE_EDGE_RISING: | |
70 | + newvalue = S5P_IRQ_TYPE_EDGE_RISING; | |
71 | + break; | |
72 | + | |
73 | + case IRQ_TYPE_EDGE_FALLING: | |
74 | + newvalue = S5P_IRQ_TYPE_EDGE_FALLING; | |
75 | + break; | |
76 | + | |
77 | + case IRQ_TYPE_EDGE_BOTH: | |
78 | + newvalue = S5P_IRQ_TYPE_EDGE_BOTH; | |
79 | + break; | |
80 | + | |
81 | + case IRQ_TYPE_LEVEL_LOW: | |
82 | + newvalue = S5P_IRQ_TYPE_LEVEL_LOW; | |
83 | + break; | |
84 | + | |
85 | + case IRQ_TYPE_LEVEL_HIGH: | |
86 | + newvalue = S5P_IRQ_TYPE_LEVEL_HIGH; | |
87 | + break; | |
88 | + | |
89 | + default: | |
90 | + printk(KERN_ERR "No such irq type %d", type); | |
91 | + return -EINVAL; | |
92 | + } | |
93 | + | |
94 | + shift = (offs & 0x7) * 4; | |
95 | + mask = 0x7 << shift; | |
96 | + | |
97 | + ctrl = __raw_readl(S5P_EINT_CON(EINT_REG_NR(data->irq))); | |
98 | + ctrl &= ~mask; | |
99 | + ctrl |= newvalue << shift; | |
100 | + __raw_writel(ctrl, S5P_EINT_CON(EINT_REG_NR(data->irq))); | |
101 | + | |
102 | + if ((0 <= offs) && (offs < 8)) | |
103 | + s3c_gpio_cfgpin(EINT_GPIO_0(offs & 0x7), EINT_MODE); | |
104 | + | |
105 | + else if ((8 <= offs) && (offs < 16)) | |
106 | + s3c_gpio_cfgpin(EINT_GPIO_1(offs & 0x7), EINT_MODE); | |
107 | + | |
108 | + else if ((16 <= offs) && (offs < 24)) | |
109 | + s3c_gpio_cfgpin(EINT_GPIO_2(offs & 0x7), EINT_MODE); | |
110 | + | |
111 | + else if ((24 <= offs) && (offs < 32)) | |
112 | + s3c_gpio_cfgpin(EINT_GPIO_3(offs & 0x7), EINT_MODE); | |
113 | + | |
114 | + else | |
115 | + printk(KERN_ERR "No such irq number %d", offs); | |
116 | + | |
117 | + return 0; | |
118 | +} | |
119 | + | |
120 | +static struct irq_chip s5p_irq_eint = { | |
121 | + .name = "s5p-eint", | |
122 | + .irq_mask = s5p_irq_eint_mask, | |
123 | + .irq_unmask = s5p_irq_eint_unmask, | |
124 | + .irq_mask_ack = s5p_irq_eint_maskack, | |
125 | + .irq_ack = s5p_irq_eint_ack, | |
126 | + .irq_set_type = s5p_irq_eint_set_type, | |
127 | +#ifdef CONFIG_PM | |
128 | + .irq_set_wake = s3c_irqext_wake, | |
129 | +#endif | |
130 | +}; | |
131 | + | |
132 | +/* s5p_irq_demux_eint | |
133 | + * | |
134 | + * This function demuxes the IRQ from the group0 external interrupts, | |
135 | + * from EINTs 16 to 31. It is designed to be inlined into the specific | |
136 | + * handler s5p_irq_demux_eintX_Y. | |
137 | + * | |
138 | + * Each EINT pend/mask registers handle eight of them. | |
139 | + */ | |
140 | +static inline void s5p_irq_demux_eint(unsigned int start) | |
141 | +{ | |
142 | + u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start))); | |
143 | + u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start))); | |
144 | + unsigned int irq; | |
145 | + | |
146 | + status &= ~mask; | |
147 | + status &= 0xff; | |
148 | + | |
149 | + while (status) { | |
150 | + irq = fls(status) - 1; | |
151 | + generic_handle_irq(irq + start); | |
152 | + status &= ~(1 << irq); | |
153 | + } | |
154 | +} | |
155 | + | |
156 | +static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc) | |
157 | +{ | |
158 | + s5p_irq_demux_eint(IRQ_EINT(16)); | |
159 | + s5p_irq_demux_eint(IRQ_EINT(24)); | |
160 | +} | |
161 | + | |
162 | +static inline void s5p_irq_vic_eint_mask(struct irq_data *data) | |
163 | +{ | |
164 | + void __iomem *base = irq_data_get_irq_chip_data(data); | |
165 | + | |
166 | + s5p_irq_eint_mask(data); | |
167 | + writel(1 << EINT_OFFSET(data->irq), base + VIC_INT_ENABLE_CLEAR); | |
168 | +} | |
169 | + | |
170 | +static void s5p_irq_vic_eint_unmask(struct irq_data *data) | |
171 | +{ | |
172 | + void __iomem *base = irq_data_get_irq_chip_data(data); | |
173 | + | |
174 | + s5p_irq_eint_unmask(data); | |
175 | + writel(1 << EINT_OFFSET(data->irq), base + VIC_INT_ENABLE); | |
176 | +} | |
177 | + | |
178 | +static inline void s5p_irq_vic_eint_ack(struct irq_data *data) | |
179 | +{ | |
180 | + __raw_writel(eint_irq_to_bit(data->irq), | |
181 | + S5P_EINT_PEND(EINT_REG_NR(data->irq))); | |
182 | +} | |
183 | + | |
184 | +static void s5p_irq_vic_eint_maskack(struct irq_data *data) | |
185 | +{ | |
186 | + s5p_irq_vic_eint_mask(data); | |
187 | + s5p_irq_vic_eint_ack(data); | |
188 | +} | |
189 | + | |
190 | +static struct irq_chip s5p_irq_vic_eint = { | |
191 | + .name = "s5p_vic_eint", | |
192 | + .irq_mask = s5p_irq_vic_eint_mask, | |
193 | + .irq_unmask = s5p_irq_vic_eint_unmask, | |
194 | + .irq_mask_ack = s5p_irq_vic_eint_maskack, | |
195 | + .irq_ack = s5p_irq_vic_eint_ack, | |
196 | + .irq_set_type = s5p_irq_eint_set_type, | |
197 | +#ifdef CONFIG_PM | |
198 | + .irq_set_wake = s3c_irqext_wake, | |
199 | +#endif | |
200 | +}; | |
201 | + | |
202 | +static int __init s5p_init_irq_eint(void) | |
203 | +{ | |
204 | + int irq; | |
205 | + | |
206 | + for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++) | |
207 | + irq_set_chip(irq, &s5p_irq_vic_eint); | |
208 | + | |
209 | + for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) { | |
210 | + irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq); | |
211 | + set_irq_flags(irq, IRQF_VALID); | |
212 | + } | |
213 | + | |
214 | + irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31); | |
215 | + return 0; | |
216 | +} | |
217 | + | |
218 | +arch_initcall(s5p_init_irq_eint); |
arch/arm/plat-samsung/s5p-irq-gpioint.c
1 | +/* | |
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> | |
6 | + * | |
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. | |
11 | + * | |
12 | + */ | |
13 | + | |
14 | +#include <linux/kernel.h> | |
15 | +#include <linux/interrupt.h> | |
16 | +#include <linux/irq.h> | |
17 | +#include <linux/io.h> | |
18 | +#include <linux/gpio.h> | |
19 | +#include <linux/slab.h> | |
20 | + | |
21 | +#include <mach/map.h> | |
22 | +#include <plat/gpio-core.h> | |
23 | +#include <plat/gpio-cfg.h> | |
24 | + | |
25 | +#include <asm/mach/irq.h> | |
26 | + | |
27 | +#define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u) | |
28 | + | |
29 | +#define CON_OFFSET 0x700 | |
30 | +#define MASK_OFFSET 0x900 | |
31 | +#define PEND_OFFSET 0xA00 | |
32 | +#define REG_OFFSET(x) ((x) << 2) | |
33 | + | |
34 | +struct s5p_gpioint_bank { | |
35 | + struct list_head list; | |
36 | + int start; | |
37 | + int nr_groups; | |
38 | + int irq; | |
39 | + struct samsung_gpio_chip **chips; | |
40 | + void (*handler)(unsigned int, struct irq_desc *); | |
41 | +}; | |
42 | + | |
43 | +static LIST_HEAD(banks); | |
44 | + | |
45 | +static int s5p_gpioint_set_type(struct irq_data *d, unsigned int type) | |
46 | +{ | |
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; | |
50 | + | |
51 | + switch (type) { | |
52 | + case IRQ_TYPE_EDGE_RISING: | |
53 | + type = S5P_IRQ_TYPE_EDGE_RISING; | |
54 | + break; | |
55 | + case IRQ_TYPE_EDGE_FALLING: | |
56 | + type = S5P_IRQ_TYPE_EDGE_FALLING; | |
57 | + break; | |
58 | + case IRQ_TYPE_EDGE_BOTH: | |
59 | + type = S5P_IRQ_TYPE_EDGE_BOTH; | |
60 | + break; | |
61 | + case IRQ_TYPE_LEVEL_HIGH: | |
62 | + type = S5P_IRQ_TYPE_LEVEL_HIGH; | |
63 | + break; | |
64 | + case IRQ_TYPE_LEVEL_LOW: | |
65 | + type = S5P_IRQ_TYPE_LEVEL_LOW; | |
66 | + break; | |
67 | + case IRQ_TYPE_NONE: | |
68 | + default: | |
69 | + printk(KERN_WARNING "No irq type\n"); | |
70 | + return -EINVAL; | |
71 | + } | |
72 | + | |
73 | + gc->type_cache &= ~(0x7 << shift); | |
74 | + gc->type_cache |= type << shift; | |
75 | + writel(gc->type_cache, gc->reg_base + ct->regs.type); | |
76 | + return 0; | |
77 | +} | |
78 | + | |
79 | +static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) | |
80 | +{ | |
81 | + struct s5p_gpioint_bank *bank = irq_get_handler_data(irq); | |
82 | + int group, pend_offset, mask_offset; | |
83 | + unsigned int pend, mask; | |
84 | + | |
85 | + struct irq_chip *chip = irq_get_chip(irq); | |
86 | + chained_irq_enter(chip, desc); | |
87 | + | |
88 | + for (group = 0; group < bank->nr_groups; group++) { | |
89 | + struct samsung_gpio_chip *chip = bank->chips[group]; | |
90 | + if (!chip) | |
91 | + continue; | |
92 | + | |
93 | + pend_offset = REG_OFFSET(group); | |
94 | + pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset); | |
95 | + if (!pend) | |
96 | + continue; | |
97 | + | |
98 | + mask_offset = REG_OFFSET(group); | |
99 | + mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset); | |
100 | + pend &= ~mask; | |
101 | + | |
102 | + while (pend) { | |
103 | + int offset = fls(pend) - 1; | |
104 | + int real_irq = chip->irq_base + offset; | |
105 | + generic_handle_irq(real_irq); | |
106 | + pend &= ~BIT(offset); | |
107 | + } | |
108 | + } | |
109 | + chained_irq_exit(chip, desc); | |
110 | +} | |
111 | + | |
112 | +static __init int s5p_gpioint_add(struct samsung_gpio_chip *chip) | |
113 | +{ | |
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; | |
119 | + | |
120 | + if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) | |
121 | + return -ENOMEM; | |
122 | + | |
123 | + list_for_each_entry(b, &banks, list) { | |
124 | + if (group >= b->start && group < b->start + b->nr_groups) { | |
125 | + bank = b; | |
126 | + break; | |
127 | + } | |
128 | + } | |
129 | + if (!bank) | |
130 | + return -EINVAL; | |
131 | + | |
132 | + if (!bank->handler) { | |
133 | + bank->chips = kzalloc(sizeof(struct samsung_gpio_chip *) * | |
134 | + bank->nr_groups, GFP_KERNEL); | |
135 | + if (!bank->chips) | |
136 | + return -ENOMEM; | |
137 | + | |
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", | |
142 | + bank->irq); | |
143 | + } | |
144 | + | |
145 | + /* | |
146 | + * chained GPIO irq has been successfully registered, allocate new gpio | |
147 | + * int group and assign irq nubmers | |
148 | + */ | |
149 | + chip->irq_base = S5P_GPIOINT_BASE + | |
150 | + used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE; | |
151 | + used_gpioint_groups++; | |
152 | + | |
153 | + bank->chips[group - bank->start] = chip; | |
154 | + | |
155 | + gc = irq_alloc_generic_chip("s5p_gpioint", 1, chip->irq_base, | |
156 | + (void __iomem *)GPIO_BASE(chip), | |
157 | + handle_level_irq); | |
158 | + if (!gc) | |
159 | + return -ENOMEM; | |
160 | + ct = gc->chip_types; | |
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); | |
171 | + return 0; | |
172 | +} | |
173 | + | |
174 | +int __init s5p_register_gpio_interrupt(int pin) | |
175 | +{ | |
176 | + struct samsung_gpio_chip *my_chip = samsung_gpiolib_getchip(pin); | |
177 | + int offset, group; | |
178 | + int ret; | |
179 | + | |
180 | + if (!my_chip) | |
181 | + return -EINVAL; | |
182 | + | |
183 | + offset = pin - my_chip->chip.base; | |
184 | + group = my_chip->group; | |
185 | + | |
186 | + /* check if the group has been already registered */ | |
187 | + if (my_chip->irq_base) | |
188 | + return my_chip->irq_base + offset; | |
189 | + | |
190 | + /* register gpio group */ | |
191 | + ret = s5p_gpioint_add(my_chip); | |
192 | + if (ret == 0) { | |
193 | + my_chip->chip.to_irq = samsung_gpiolib_to_irq; | |
194 | + printk(KERN_INFO "Registered interrupt support for gpio group %d.\n", | |
195 | + group); | |
196 | + return my_chip->irq_base + offset; | |
197 | + } | |
198 | + return ret; | |
199 | +} | |
200 | + | |
201 | +int __init s5p_register_gpioint_bank(int chain_irq, int start, int nr_groups) | |
202 | +{ | |
203 | + struct s5p_gpioint_bank *bank; | |
204 | + | |
205 | + bank = kzalloc(sizeof(*bank), GFP_KERNEL); | |
206 | + if (!bank) | |
207 | + return -ENOMEM; | |
208 | + | |
209 | + bank->start = start; | |
210 | + bank->nr_groups = nr_groups; | |
211 | + bank->irq = chain_irq; | |
212 | + | |
213 | + list_add_tail(&bank->list, &banks); | |
214 | + return 0; | |
215 | +} |
arch/arm/plat-samsung/s5p-irq.c
1 | +/* | |
2 | + * Copyright (c) 2009 Samsung Electronics Co., Ltd. | |
3 | + * http://www.samsung.com/ | |
4 | + * | |
5 | + * S5P - Interrupt handling | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify | |
8 | + * it under the terms of the GNU General Public License version 2 as | |
9 | + * published by the Free Software Foundation. | |
10 | +*/ | |
11 | + | |
12 | +#include <linux/kernel.h> | |
13 | +#include <linux/interrupt.h> | |
14 | +#include <linux/irq.h> | |
15 | +#include <linux/io.h> | |
16 | + | |
17 | +#include <asm/hardware/vic.h> | |
18 | + | |
19 | +#include <mach/map.h> | |
20 | +#include <plat/regs-timer.h> | |
21 | +#include <plat/cpu.h> | |
22 | +#include <plat/irq-vic-timer.h> | |
23 | + | |
24 | +void __init s5p_init_irq(u32 *vic, u32 num_vic) | |
25 | +{ | |
26 | +#ifdef CONFIG_ARM_VIC | |
27 | + int irq; | |
28 | + | |
29 | + /* initialize the VICs */ | |
30 | + for (irq = 0; irq < num_vic; irq++) | |
31 | + vic_init(VA_VIC(irq), VIC_BASE(irq), vic[irq], 0); | |
32 | +#endif | |
33 | + | |
34 | + s3c_init_vic_timer_irq(5, IRQ_TIMER0); | |
35 | +} |