Linux-libre 3.10.98-gnu
[librecmc/linux-libre.git] / 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 #include <linux/irqchip/arm-vic.h>
19
20 #include <plat/regs-irqtype.h>
21
22 #include <mach/map.h>
23 #include <plat/cpu.h>
24 #include <plat/pm.h>
25
26 #include <plat/gpio-cfg.h>
27 #include <mach/regs-gpio.h>
28
29 static inline void s5p_irq_eint_mask(struct irq_data *data)
30 {
31         u32 mask;
32
33         mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq)));
34         mask |= eint_irq_to_bit(data->irq);
35         __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq)));
36 }
37
38 static void s5p_irq_eint_unmask(struct irq_data *data)
39 {
40         u32 mask;
41
42         mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(data->irq)));
43         mask &= ~(eint_irq_to_bit(data->irq));
44         __raw_writel(mask, S5P_EINT_MASK(EINT_REG_NR(data->irq)));
45 }
46
47 static inline void s5p_irq_eint_ack(struct irq_data *data)
48 {
49         __raw_writel(eint_irq_to_bit(data->irq),
50                      S5P_EINT_PEND(EINT_REG_NR(data->irq)));
51 }
52
53 static void s5p_irq_eint_maskack(struct irq_data *data)
54 {
55         /* compiler should in-line these */
56         s5p_irq_eint_mask(data);
57         s5p_irq_eint_ack(data);
58 }
59
60 static int s5p_irq_eint_set_type(struct irq_data *data, unsigned int type)
61 {
62         int offs = EINT_OFFSET(data->irq);
63         int shift;
64         u32 ctrl, mask;
65         u32 newvalue = 0;
66
67         switch (type) {
68         case IRQ_TYPE_EDGE_RISING:
69                 newvalue = S5P_IRQ_TYPE_EDGE_RISING;
70                 break;
71
72         case IRQ_TYPE_EDGE_FALLING:
73                 newvalue = S5P_IRQ_TYPE_EDGE_FALLING;
74                 break;
75
76         case IRQ_TYPE_EDGE_BOTH:
77                 newvalue = S5P_IRQ_TYPE_EDGE_BOTH;
78                 break;
79
80         case IRQ_TYPE_LEVEL_LOW:
81                 newvalue = S5P_IRQ_TYPE_LEVEL_LOW;
82                 break;
83
84         case IRQ_TYPE_LEVEL_HIGH:
85                 newvalue = S5P_IRQ_TYPE_LEVEL_HIGH;
86                 break;
87
88         default:
89                 printk(KERN_ERR "No such irq type %d", type);
90                 return -EINVAL;
91         }
92
93         shift = (offs & 0x7) * 4;
94         mask = 0x7 << shift;
95
96         ctrl = __raw_readl(S5P_EINT_CON(EINT_REG_NR(data->irq)));
97         ctrl &= ~mask;
98         ctrl |= newvalue << shift;
99         __raw_writel(ctrl, S5P_EINT_CON(EINT_REG_NR(data->irq)));
100
101         if ((0 <= offs) && (offs < 8))
102                 s3c_gpio_cfgpin(EINT_GPIO_0(offs & 0x7), EINT_MODE);
103
104         else if ((8 <= offs) && (offs < 16))
105                 s3c_gpio_cfgpin(EINT_GPIO_1(offs & 0x7), EINT_MODE);
106
107         else if ((16 <= offs) && (offs < 24))
108                 s3c_gpio_cfgpin(EINT_GPIO_2(offs & 0x7), EINT_MODE);
109
110         else if ((24 <= offs) && (offs < 32))
111                 s3c_gpio_cfgpin(EINT_GPIO_3(offs & 0x7), EINT_MODE);
112
113         else
114                 printk(KERN_ERR "No such irq number %d", offs);
115
116         return 0;
117 }
118
119 static struct irq_chip s5p_irq_eint = {
120         .name           = "s5p-eint",
121         .irq_mask       = s5p_irq_eint_mask,
122         .irq_unmask     = s5p_irq_eint_unmask,
123         .irq_mask_ack   = s5p_irq_eint_maskack,
124         .irq_ack        = s5p_irq_eint_ack,
125         .irq_set_type   = s5p_irq_eint_set_type,
126 #ifdef CONFIG_PM
127         .irq_set_wake   = s3c_irqext_wake,
128 #endif
129 };
130
131 /* s5p_irq_demux_eint
132  *
133  * This function demuxes the IRQ from the group0 external interrupts,
134  * from EINTs 16 to 31. It is designed to be inlined into the specific
135  * handler s5p_irq_demux_eintX_Y.
136  *
137  * Each EINT pend/mask registers handle eight of them.
138  */
139 static inline void s5p_irq_demux_eint(unsigned int start)
140 {
141         u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start)));
142         u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start)));
143         unsigned int irq;
144
145         status &= ~mask;
146         status &= 0xff;
147
148         while (status) {
149                 irq = fls(status) - 1;
150                 generic_handle_irq(irq + start);
151                 status &= ~(1 << irq);
152         }
153 }
154
155 static void s5p_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
156 {
157         s5p_irq_demux_eint(IRQ_EINT(16));
158         s5p_irq_demux_eint(IRQ_EINT(24));
159 }
160
161 static inline void s5p_irq_vic_eint_mask(struct irq_data *data)
162 {
163         void __iomem *base = irq_data_get_irq_chip_data(data);
164
165         s5p_irq_eint_mask(data);
166         writel(1 << EINT_OFFSET(data->irq), base + VIC_INT_ENABLE_CLEAR);
167 }
168
169 static void s5p_irq_vic_eint_unmask(struct irq_data *data)
170 {
171         void __iomem *base = irq_data_get_irq_chip_data(data);
172
173         s5p_irq_eint_unmask(data);
174         writel(1 << EINT_OFFSET(data->irq), base + VIC_INT_ENABLE);
175 }
176
177 static inline void s5p_irq_vic_eint_ack(struct irq_data *data)
178 {
179         __raw_writel(eint_irq_to_bit(data->irq),
180                      S5P_EINT_PEND(EINT_REG_NR(data->irq)));
181 }
182
183 static void s5p_irq_vic_eint_maskack(struct irq_data *data)
184 {
185         s5p_irq_vic_eint_mask(data);
186         s5p_irq_vic_eint_ack(data);
187 }
188
189 static struct irq_chip s5p_irq_vic_eint = {
190         .name           = "s5p_vic_eint",
191         .irq_mask       = s5p_irq_vic_eint_mask,
192         .irq_unmask     = s5p_irq_vic_eint_unmask,
193         .irq_mask_ack   = s5p_irq_vic_eint_maskack,
194         .irq_ack        = s5p_irq_vic_eint_ack,
195         .irq_set_type   = s5p_irq_eint_set_type,
196 #ifdef CONFIG_PM
197         .irq_set_wake   = s3c_irqext_wake,
198 #endif
199 };
200
201 static int __init s5p_init_irq_eint(void)
202 {
203         int irq;
204
205         for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
206                 irq_set_chip(irq, &s5p_irq_vic_eint);
207
208         for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
209                 irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
210                 set_irq_flags(irq, IRQF_VALID);
211         }
212
213         irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
214         return 0;
215 }
216
217 arch_initcall(s5p_init_irq_eint);