xtensa: support s6000 gpio irqs and alternate function selection
[pandora-kernel.git] / arch / xtensa / variants / s6000 / gpio.c
1 /*
2  * s6000 gpio driver
3  *
4  * Copyright (c) 2009 emlix GmbH
5  * Authors:     Oskar Schirmer <os@emlix.com>
6  *              Johannes Weiner <jw@emlix.com>
7  *              Daniel Gloeckner <dg@emlix.com>
8  */
9 #include <linux/bitops.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/io.h>
14 #include <linux/irq.h>
15 #include <linux/gpio.h>
16
17 #include <variant/hardware.h>
18
19 #define IRQ_BASE XTENSA_NR_IRQS
20
21 #define S6_GPIO_DATA            0x000
22 #define S6_GPIO_IS              0x404
23 #define S6_GPIO_IBE             0x408
24 #define S6_GPIO_IEV             0x40C
25 #define S6_GPIO_IE              0x410
26 #define S6_GPIO_RIS             0x414
27 #define S6_GPIO_MIS             0x418
28 #define S6_GPIO_IC              0x41C
29 #define S6_GPIO_AFSEL           0x420
30 #define S6_GPIO_DIR             0x800
31 #define S6_GPIO_BANK(nr)        ((nr) * 0x1000)
32 #define S6_GPIO_MASK(nr)        (4 << (nr))
33 #define S6_GPIO_OFFSET(nr) \
34                 (S6_GPIO_BANK((nr) >> 3) + S6_GPIO_MASK((nr) & 7))
35
36 static int direction_input(struct gpio_chip *chip, unsigned int off)
37 {
38         writeb(0, S6_REG_GPIO + S6_GPIO_DIR + S6_GPIO_OFFSET(off));
39         return 0;
40 }
41
42 static int get(struct gpio_chip *chip, unsigned int off)
43 {
44         return readb(S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
45 }
46
47 static int direction_output(struct gpio_chip *chip, unsigned int off, int val)
48 {
49         unsigned rel = S6_GPIO_OFFSET(off);
50         writeb(~0, S6_REG_GPIO + S6_GPIO_DIR + rel);
51         writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + rel);
52         return 0;
53 }
54
55 static void set(struct gpio_chip *chip, unsigned int off, int val)
56 {
57         writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
58 }
59
60 static int to_irq(struct gpio_chip *chip, unsigned offset)
61 {
62         if (offset < 8)
63                 return offset + IRQ_BASE;
64         return -EINVAL;
65 }
66
67 static struct gpio_chip gpiochip = {
68         .owner = THIS_MODULE,
69         .direction_input = direction_input,
70         .get = get,
71         .direction_output = direction_output,
72         .set = set,
73         .to_irq = to_irq,
74         .base = 0,
75         .ngpio = 24,
76         .can_sleep = 0, /* no blocking io needed */
77         .exported = 0, /* no exporting to userspace */
78 };
79
80 int s6_gpio_init(u32 afsel)
81 {
82         writeb(afsel, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL);
83         writeb(afsel >> 8, S6_REG_GPIO + S6_GPIO_BANK(1) + S6_GPIO_AFSEL);
84         writeb(afsel >> 16, S6_REG_GPIO + S6_GPIO_BANK(2) + S6_GPIO_AFSEL);
85         return gpiochip_add(&gpiochip);
86 }
87
88 static void ack(unsigned int irq)
89 {
90         writeb(1 << (irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
91 }
92
93 static void mask(unsigned int irq)
94 {
95         u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);
96         r &= ~(1 << (irq - IRQ_BASE));
97         writeb(r, S6_REG_GPIO + S6_GPIO_IE);
98 }
99
100 static void unmask(unsigned int irq)
101 {
102         u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);
103         m |= 1 << (irq - IRQ_BASE);
104         writeb(m, S6_REG_GPIO + S6_GPIO_IE);
105 }
106
107 static int set_type(unsigned int irq, unsigned int type)
108 {
109         const u8 m = 1 << (irq - IRQ_BASE);
110         irq_flow_handler_t handler;
111         struct irq_desc *desc;
112         u8 reg;
113
114         if (type == IRQ_TYPE_PROBE) {
115                 if ((readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL) & m)
116                     || (readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE) & m)
117                     || readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_DIR
118                               + S6_GPIO_MASK(irq - IRQ_BASE)))
119                         return 0;
120                 type = IRQ_TYPE_EDGE_BOTH;
121         }
122
123         reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
124         if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
125                 reg |= m;
126                 handler = handle_level_irq;
127         } else {
128                 reg &= ~m;
129                 handler = handle_edge_irq;
130         }
131         writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
132         desc = irq_to_desc(irq);
133         desc->handle_irq = handler;
134
135         reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
136         if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING))
137                 reg |= m;
138         else
139                 reg &= ~m;
140         writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
141
142         reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
143         if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
144                 reg |= m;
145         else
146                 reg &= ~m;
147         writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
148         return 0;
149 }
150
151 static struct irq_chip gpioirqs = {
152         .name = "GPIO",
153         .ack = ack,
154         .mask = mask,
155         .unmask = unmask,
156         .set_type = set_type,
157 };
158
159 static u8 demux_masks[4];
160
161 static void demux_irqs(unsigned int irq, struct irq_desc *desc)
162 {
163         u8 *mask = get_irq_desc_data(desc);
164         u8 pending;
165         int cirq;
166
167         desc->chip->mask(irq);
168         desc->chip->ack(irq);
169         pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask;
170         cirq = IRQ_BASE - 1;
171         while (pending) {
172                 int n = ffs(pending);
173                 cirq += n;
174                 pending >>= n;
175                 generic_handle_irq(cirq);
176         }
177         desc->chip->unmask(irq);
178 }
179
180 extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];
181
182 void __init variant_init_irq(void)
183 {
184         int irq, n;
185         writeb(0, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE);
186         for (irq = n = 0; irq < XTENSA_NR_IRQS; irq++) {
187                 const signed char *mapping = platform_irq_mappings[irq];
188                 int alone = 1;
189                 u8 mask;
190                 if (!mapping)
191                         continue;
192                 for(mask = 0; *mapping != -1; mapping++)
193                         switch (*mapping) {
194                         case S6_INTC_GPIO(0):
195                                 mask |= 1 << 0;
196                                 break;
197                         case S6_INTC_GPIO(1):
198                                 mask |= 1 << 1;
199                                 break;
200                         case S6_INTC_GPIO(2):
201                                 mask |= 1 << 2;
202                                 break;
203                         case S6_INTC_GPIO(3):
204                                 mask |= 0x1f << 3;
205                                 break;
206                         default:
207                                 alone = 0;
208                         }
209                 if (mask) {
210                         int cirq, i;
211                         if (!alone) {
212                                 printk(KERN_ERR "chained irq chips can't share"
213                                         " parent irq %i\n", irq);
214                                 continue;
215                         }
216                         demux_masks[n] = mask;
217                         cirq = IRQ_BASE - 1;
218                         do {
219                                 i = ffs(mask);
220                                 cirq += i;
221                                 mask >>= i;
222                                 set_irq_chip(cirq, &gpioirqs);
223                                 set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
224                         } while (mask);
225                         set_irq_data(irq, demux_masks + n);
226                         set_irq_chained_handler(irq, demux_irqs);
227                         if (++n == ARRAY_SIZE(demux_masks))
228                                 break;
229                 }
230         }
231 }