2 * linux/arch/arm/plat-samsung/irq-gpio.c
4 * Copyright (C) 2009-2010 Samsung Electronics Co.Ltd
5 * Author: Kyungmin Park <kyungmin.park@samsung.com>
6 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
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.
13 #include <linux/kernel.h>
14 #include <linux/init.h>
15 #include <linux/irq.h>
16 #include <linux/gpio.h>
17 #include <linux/slab.h>
20 #include <plat/gpio-core.h>
22 #define GPIO_BASE(chip) (((unsigned long)(chip)->base) & ~(SZ_4K - 1))
24 #define CON_OFFSET 0x700
25 #define MASK_OFFSET 0x900
26 #define PEND_OFFSET 0xA00
27 #define REG_OFFSET(x) ((x) << 2)
29 #define SAMSUNG_IRQ_GPIO_LEVEL_LOW 0x0
30 #define SAMSUNG_IRQ_GPIO_LEVEL_HIGH 0x1
31 #define SAMSUNG_IRQ_GPIO_EDGE_FALLING 0x2
32 #define SAMSUNG_IRQ_GPIO_EDGE_RISING 0x3
33 #define SAMSUNG_IRQ_GPIO_EDGE_BOTH 0x4
35 #define IRQ_GPIO_GROUP(x) (IRQ_GPIO_BASE + ((x) * 8))
37 static inline int samsung_irq_gpio_get_bit(unsigned int irq, int group)
39 return irq - (IRQ_GPIO_GROUP(group));
42 static void samsung_irq_gpio_ack(unsigned int irq)
44 struct s3c_gpio_chip *chip = get_irq_chip_data(irq);
45 int group, localgroup, n, offset, value;
48 localgroup = chip->localgroup;
49 n = samsung_irq_gpio_get_bit(irq, group);
51 offset = REG_OFFSET(localgroup); /* 4 bytes offset */
53 value = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + offset);
55 __raw_writel(value, GPIO_BASE(chip) + PEND_OFFSET + offset);
58 static void samsung_irq_gpio_mask(unsigned int irq)
60 struct s3c_gpio_chip *chip = get_irq_chip_data(irq);
61 int group, localgroup, n, offset, value;
64 localgroup = chip->localgroup;
65 n = samsung_irq_gpio_get_bit(irq, group);
67 offset = REG_OFFSET(localgroup); /* 4 bytes offset */
69 value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + offset);
71 __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + offset);
74 static void samsung_irq_gpio_mask_ack(unsigned int irq)
76 samsung_irq_gpio_mask(irq);
77 samsung_irq_gpio_ack(irq);
80 static void samsung_irq_gpio_unmask(unsigned int irq)
82 struct s3c_gpio_chip *chip = get_irq_chip_data(irq);
83 int group, localgroup, n, offset, value;
86 localgroup = chip->localgroup;
87 n = samsung_irq_gpio_get_bit(irq, group);
89 offset = REG_OFFSET(localgroup); /* 4 bytes offset */
91 value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + offset);
93 __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + offset);
96 static int samsung_irq_gpio_set_type(unsigned int irq, unsigned int type)
98 struct s3c_gpio_chip *chip = get_irq_chip_data(irq);
99 int group, localgroup, bit, offset, value;
100 struct irq_desc *desc = irq_to_desc(irq);
103 localgroup = chip->localgroup;
104 bit = samsung_irq_gpio_get_bit(irq, group);
107 case IRQ_TYPE_EDGE_RISING:
108 type = SAMSUNG_IRQ_GPIO_EDGE_RISING;
110 case IRQ_TYPE_EDGE_FALLING:
111 type = SAMSUNG_IRQ_GPIO_EDGE_FALLING;
113 case IRQ_TYPE_EDGE_BOTH:
114 type = SAMSUNG_IRQ_GPIO_EDGE_BOTH;
116 case IRQ_TYPE_LEVEL_HIGH:
117 type = SAMSUNG_IRQ_GPIO_LEVEL_HIGH;
119 case IRQ_TYPE_LEVEL_LOW:
120 type = SAMSUNG_IRQ_GPIO_LEVEL_LOW;
123 printk(KERN_WARNING "No irq type\n");
128 offset = REG_OFFSET(localgroup); /* 4 bytes offset */
129 bit = bit << 2; /* 4 bits offset */
131 value = __raw_readl(GPIO_BASE(chip) + CON_OFFSET + offset);
132 value &= ~(0xf << bit);
133 value |= (type << bit);
134 __raw_writel(value, GPIO_BASE(chip) + CON_OFFSET + offset);
136 if (type & IRQ_TYPE_EDGE_BOTH)
137 desc->handle_irq = handle_edge_irq;
139 desc->handle_irq = handle_level_irq;
144 static struct irq_chip samsung_irq_gpio = {
146 .ack = samsung_irq_gpio_ack,
147 .mask = samsung_irq_gpio_mask,
148 .mask_ack = samsung_irq_gpio_mask_ack,
149 .unmask = samsung_irq_gpio_unmask,
150 .set_type = samsung_irq_gpio_set_type,
153 static int samsung_irq_gpio_to_irq(struct gpio_chip *gpio, unsigned int offset)
155 struct s3c_gpio_chip *chip = to_s3c_gpio(gpio);
157 return IRQ_GPIO_GROUP(chip->group) + offset;
160 void __init samsung_irq_gpio_add(struct s3c_gpio_chip *chip)
164 chip->chip.to_irq = samsung_irq_gpio_to_irq;
166 for (i = 0; i < chip->chip.ngpio; i++) {
167 irq = IRQ_GPIO_GROUP(chip->group) + i;
168 set_irq_chip(irq, &samsung_irq_gpio);
169 set_irq_chip_data(irq, chip);
170 set_irq_handler(irq, handle_level_irq);
171 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
176 * Note that this 'irq' is the real repesentative irq for GPIO
178 * To reduce the register access, use the valid group cache.
179 * first check the valid groups. if failed, scan all groups fully.
181 static void samsung_irq_gpio_handler(unsigned int irq, struct irq_desc *desc)
183 struct samsung_irq_gpio *gpio;
184 int group, n, offset;
185 int start, end, pend, mask, handled = 0;
186 struct irq_chip *chip = get_irq_chip(irq);
188 gpio = get_irq_data(irq);
190 end = gpio->nr_groups;
192 /* primary controller ack'ing */
196 /* Check the valid group first */
197 for (group = 0; group <= end; group++) {
198 if (!test_bit(group, &gpio->valid_groups))
201 offset = REG_OFFSET(group); /* 4 bytes offset */
202 pend = __raw_readl(gpio->base + PEND_OFFSET + offset);
206 mask = __raw_readl(gpio->base + MASK_OFFSET + offset);
213 generic_handle_irq(IRQ_GPIO_GROUP(start + group) + n);
223 /* Okay we can't find a proper handler. Scan fully */
224 for (group = 0; group <= end; group++) {
225 offset = REG_OFFSET(group); /* 4 bytes offset */
226 pend = __raw_readl(gpio->base + PEND_OFFSET + offset);
230 mask = __raw_readl(gpio->base + MASK_OFFSET + offset);
237 generic_handle_irq(IRQ_GPIO_GROUP(start + group) + n);
241 /* It found the valid group */
242 set_bit(group, &gpio->valid_groups);
246 /* primary controller unmasking */
250 void __init samsung_irq_gpio_register(struct samsung_irq_gpio_info *gpios, int num)
252 struct samsung_irq_gpio_info *gpio = gpios;
253 struct samsung_irq_gpio *sig;
256 for (i = 0; i < num; i++, gpio++) {
257 sig = kmemdup(&gpio->sig, sizeof(gpio->sig), GFP_KERNEL);
261 sig->valid_groups = 0;
262 /* Use the default irq gpio handler, if no handler is defined */
264 gpio->handler = samsung_irq_gpio_handler;
266 set_irq_chained_handler(gpio->irq, gpio->handler);
267 set_irq_data(gpio->irq, sig);