• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/arch/arm/mach-s5pc100/
1/*
2 * arch/arm/mach-s5pc100/irq-gpio.c
3 *
4 * Copyright (C) 2009 Samsung Electronics
5 *
6 * S5PC100 - Interrupt handling for IRQ_GPIO${group}(x)
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/gpio.h>
18
19#include <mach/map.h>
20#include <plat/gpio-cfg.h>
21
22#define S5P_GPIOREG(x)		(S5P_VA_GPIO + (x))
23
24#define CON_OFFSET			0x700
25#define MASK_OFFSET			0x900
26#define PEND_OFFSET			0xA00
27#define CON_OFFSET_2			0xE00
28#define MASK_OFFSET_2			0xF00
29#define PEND_OFFSET_2			0xF40
30
31#define GPIOINT_LEVEL_LOW		0x0
32#define GPIOINT_LEVEL_HIGH		0x1
33#define GPIOINT_EDGE_FALLING		0x2
34#define GPIOINT_EDGE_RISING		0x3
35#define GPIOINT_EDGE_BOTH		0x4
36
37static int group_to_con_offset(int group)
38{
39	return group << 2;
40}
41
42static int group_to_mask_offset(int group)
43{
44	return group << 2;
45}
46
47static int group_to_pend_offset(int group)
48{
49	return group << 2;
50}
51
52static int s5pc100_get_start(unsigned int group)
53{
54	switch (group) {
55	case 0: return S5PC100_GPIO_A0_START;
56	case 1: return S5PC100_GPIO_A1_START;
57	case 2: return S5PC100_GPIO_B_START;
58	case 3: return S5PC100_GPIO_C_START;
59	case 4: return S5PC100_GPIO_D_START;
60	case 5: return S5PC100_GPIO_E0_START;
61	case 6: return S5PC100_GPIO_E1_START;
62	case 7: return S5PC100_GPIO_F0_START;
63	case 8: return S5PC100_GPIO_F1_START;
64	case 9: return S5PC100_GPIO_F2_START;
65	case 10: return S5PC100_GPIO_F3_START;
66	case 11: return S5PC100_GPIO_G0_START;
67	case 12: return S5PC100_GPIO_G1_START;
68	case 13: return S5PC100_GPIO_G2_START;
69	case 14: return S5PC100_GPIO_G3_START;
70	case 15: return S5PC100_GPIO_I_START;
71	case 16: return S5PC100_GPIO_J0_START;
72	case 17: return S5PC100_GPIO_J1_START;
73	case 18: return S5PC100_GPIO_J2_START;
74	case 19: return S5PC100_GPIO_J3_START;
75	case 20: return S5PC100_GPIO_J4_START;
76	default:
77		BUG();
78	}
79
80	return -EINVAL;
81}
82
83static int s5pc100_get_group(unsigned int irq)
84{
85	irq -= S3C_IRQ_GPIO(0);
86
87	switch (irq) {
88	case S5PC100_GPIO_A0_START ... S5PC100_GPIO_A1_START - 1:
89		return 0;
90	case S5PC100_GPIO_A1_START ... S5PC100_GPIO_B_START - 1:
91		return 1;
92	case S5PC100_GPIO_B_START ... S5PC100_GPIO_C_START - 1:
93		return 2;
94	case S5PC100_GPIO_C_START ... S5PC100_GPIO_D_START - 1:
95		return 3;
96	case S5PC100_GPIO_D_START ... S5PC100_GPIO_E0_START - 1:
97		return 4;
98	case S5PC100_GPIO_E0_START ... S5PC100_GPIO_E1_START - 1:
99		return 5;
100	case S5PC100_GPIO_E1_START ... S5PC100_GPIO_F0_START - 1:
101		return 6;
102	case S5PC100_GPIO_F0_START ... S5PC100_GPIO_F1_START - 1:
103		return 7;
104	case S5PC100_GPIO_F1_START ... S5PC100_GPIO_F2_START - 1:
105		return 8;
106	case S5PC100_GPIO_F2_START ... S5PC100_GPIO_F3_START - 1:
107		return 9;
108	case S5PC100_GPIO_F3_START ... S5PC100_GPIO_G0_START - 1:
109		return 10;
110	case S5PC100_GPIO_G0_START ... S5PC100_GPIO_G1_START - 1:
111		return 11;
112	case S5PC100_GPIO_G1_START ... S5PC100_GPIO_G2_START - 1:
113		return 12;
114	case S5PC100_GPIO_G2_START ... S5PC100_GPIO_G3_START - 1:
115		return 13;
116	case S5PC100_GPIO_G3_START ... S5PC100_GPIO_H0_START - 1:
117		return 14;
118	case S5PC100_GPIO_I_START ... S5PC100_GPIO_J0_START - 1:
119		return 15;
120	case S5PC100_GPIO_J0_START ... S5PC100_GPIO_J1_START - 1:
121		return 16;
122	case S5PC100_GPIO_J1_START ... S5PC100_GPIO_J2_START - 1:
123		return 17;
124	case S5PC100_GPIO_J2_START ... S5PC100_GPIO_J3_START - 1:
125		return 18;
126	case S5PC100_GPIO_J3_START ... S5PC100_GPIO_J4_START - 1:
127		return 19;
128	case S5PC100_GPIO_J4_START ... S5PC100_GPIO_K0_START - 1:
129		return 20;
130	default:
131		BUG();
132	}
133
134	return -EINVAL;
135}
136
137static int s5pc100_get_offset(unsigned int irq)
138{
139	struct gpio_chip *chip = get_irq_data(irq);
140	return irq - S3C_IRQ_GPIO(chip->base);
141}
142
143static void s5pc100_gpioint_ack(unsigned int irq)
144{
145	int group, offset, pend_offset;
146	unsigned int value;
147
148	group = s5pc100_get_group(irq);
149	offset = s5pc100_get_offset(irq);
150	pend_offset = group_to_pend_offset(group);
151
152	value = __raw_readl(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
153	value |= 1 << offset;
154	__raw_writel(value, S5P_GPIOREG(PEND_OFFSET) + pend_offset);
155}
156
157static void s5pc100_gpioint_mask(unsigned int irq)
158{
159	int group, offset, mask_offset;
160	unsigned int value;
161
162	group = s5pc100_get_group(irq);
163	offset = s5pc100_get_offset(irq);
164	mask_offset = group_to_mask_offset(group);
165
166	value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
167	value |= 1 << offset;
168	__raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
169}
170
171static void s5pc100_gpioint_unmask(unsigned int irq)
172{
173	int group, offset, mask_offset;
174	unsigned int value;
175
176	group = s5pc100_get_group(irq);
177	offset = s5pc100_get_offset(irq);
178	mask_offset = group_to_mask_offset(group);
179
180	value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
181	value &= ~(1 << offset);
182	__raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
183}
184
185static void s5pc100_gpioint_mask_ack(unsigned int irq)
186{
187	s5pc100_gpioint_mask(irq);
188	s5pc100_gpioint_ack(irq);
189}
190
191static int s5pc100_gpioint_set_type(unsigned int irq, unsigned int type)
192{
193	int group, offset, con_offset;
194	unsigned int value;
195
196	group = s5pc100_get_group(irq);
197	offset = s5pc100_get_offset(irq);
198	con_offset = group_to_con_offset(group);
199
200	switch (type) {
201	case IRQ_TYPE_NONE:
202		printk(KERN_WARNING "No irq type\n");
203		return -EINVAL;
204	case IRQ_TYPE_EDGE_RISING:
205		type = GPIOINT_EDGE_RISING;
206		break;
207	case IRQ_TYPE_EDGE_FALLING:
208		type = GPIOINT_EDGE_FALLING;
209		break;
210	case IRQ_TYPE_EDGE_BOTH:
211		type = GPIOINT_EDGE_BOTH;
212		break;
213	case IRQ_TYPE_LEVEL_HIGH:
214		type = GPIOINT_LEVEL_HIGH;
215		break;
216	case IRQ_TYPE_LEVEL_LOW:
217		type = GPIOINT_LEVEL_LOW;
218		break;
219	default:
220		BUG();
221	}
222
223
224	value = __raw_readl(S5P_GPIOREG(CON_OFFSET) + con_offset);
225	value &= ~(0xf << (offset * 0x4));
226	value |= (type << (offset * 0x4));
227	__raw_writel(value, S5P_GPIOREG(CON_OFFSET) + con_offset);
228
229	return 0;
230}
231
232struct irq_chip s5pc100_gpioint = {
233	.name		= "GPIO",
234	.ack		= s5pc100_gpioint_ack,
235	.mask		= s5pc100_gpioint_mask,
236	.mask_ack	= s5pc100_gpioint_mask_ack,
237	.unmask		= s5pc100_gpioint_unmask,
238	.set_type	= s5pc100_gpioint_set_type,
239};
240
241void s5pc100_irq_gpioint_handler(unsigned int irq, struct irq_desc *desc)
242{
243	int group, offset, pend_offset, mask_offset;
244	int real_irq, group_end;
245	unsigned int pend, mask;
246
247	group_end = 21;
248
249	for (group = 0; group < group_end; group++) {
250		pend_offset = group_to_pend_offset(group);
251		pend = __raw_readl(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
252		if (!pend)
253			continue;
254
255		mask_offset = group_to_mask_offset(group);
256		mask = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
257		pend &= ~mask;
258
259		for (offset = 0; offset < 8; offset++) {
260			if (pend & (1 << offset)) {
261				real_irq = s5pc100_get_start(group) + offset;
262				generic_handle_irq(S3C_IRQ_GPIO(real_irq));
263			}
264		}
265	}
266}
267