]> git.openfabrics.org - ~shefty/rdma-dev.git/blob - arch/xtensa/variants/s6000/gpio.c
xtensa: unbalanced parentheses
[~shefty/rdma-dev.git] / arch / xtensa / variants / s6000 / gpio.c
1 /*
2  * s6000 gpio driver
3  *
4  * Copyright (c) 2009 emlix GmbH
5  * Authors:     Oskar Schirmer <oskar@scara.com>
6  *              Johannes Weiner <hannes@cmpxchg.org>
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(struct irq_data *d)
89 {
90         writeb(1 << (d->irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
91 }
92
93 static void mask(struct irq_data *d)
94 {
95         u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);
96         r &= ~(1 << (d->irq - IRQ_BASE));
97         writeb(r, S6_REG_GPIO + S6_GPIO_IE);
98 }
99
100 static void unmask(struct irq_data *d)
101 {
102         u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);
103         m |= 1 << (d->irq - IRQ_BASE);
104         writeb(m, S6_REG_GPIO + S6_GPIO_IE);
105 }
106
107 static int set_type(struct irq_data *d, unsigned int type)
108 {
109         const u8 m = 1 << (d->irq - IRQ_BASE);
110         irq_flow_handler_t handler;
111         u8 reg;
112
113         if (type == IRQ_TYPE_PROBE) {
114                 if ((readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL) & m)
115                     || (readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE) & m)
116                     || readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_DIR
117                               + S6_GPIO_MASK(irq - IRQ_BASE)))
118                         return 0;
119                 type = IRQ_TYPE_EDGE_BOTH;
120         }
121
122         reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
123         if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
124                 reg |= m;
125                 handler = handle_level_irq;
126         } else {
127                 reg &= ~m;
128                 handler = handle_edge_irq;
129         }
130         writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
131         __irq_set_handler_locked(irq, handler);
132
133         reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
134         if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING))
135                 reg |= m;
136         else
137                 reg &= ~m;
138         writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
139
140         reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
141         if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
142                 reg |= m;
143         else
144                 reg &= ~m;
145         writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
146         return 0;
147 }
148
149 static struct irq_chip gpioirqs = {
150         .name = "GPIO",
151         .irq_ack = ack,
152         .irq_mask = mask,
153         .irq_unmask = unmask,
154         .irq_set_type = set_type,
155 };
156
157 static u8 demux_masks[4];
158
159 static void demux_irqs(unsigned int irq, struct irq_desc *desc)
160 {
161         struct irq_chip *chip = irq_desc_get_chip(desc);
162         u8 *mask = irq_desc_get_handler_data(desc);
163         u8 pending;
164         int cirq;
165
166         chip->irq_mask(&desc->irq_data);
167         chip->irq_ack(&desc->irq_data);
168         pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask;
169         cirq = IRQ_BASE - 1;
170         while (pending) {
171                 int n = ffs(pending);
172                 cirq += n;
173                 pending >>= n;
174                 generic_handle_irq(cirq);
175         }
176         chip->irq_unmask(&desc->irq_data);
177 }
178
179 extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];
180
181 void __init variant_init_irq(void)
182 {
183         int irq, n;
184         writeb(0, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE);
185         for (irq = n = 0; irq < XTENSA_NR_IRQS; irq++) {
186                 const signed char *mapping = platform_irq_mappings[irq];
187                 int alone = 1;
188                 u8 mask;
189                 if (!mapping)
190                         continue;
191                 for(mask = 0; *mapping != -1; mapping++)
192                         switch (*mapping) {
193                         case S6_INTC_GPIO(0):
194                                 mask |= 1 << 0;
195                                 break;
196                         case S6_INTC_GPIO(1):
197                                 mask |= 1 << 1;
198                                 break;
199                         case S6_INTC_GPIO(2):
200                                 mask |= 1 << 2;
201                                 break;
202                         case S6_INTC_GPIO(3):
203                                 mask |= 0x1f << 3;
204                                 break;
205                         default:
206                                 alone = 0;
207                         }
208                 if (mask) {
209                         int cirq, i;
210                         if (!alone) {
211                                 printk(KERN_ERR "chained irq chips can't share"
212                                         " parent irq %i\n", irq);
213                                 continue;
214                         }
215                         demux_masks[n] = mask;
216                         cirq = IRQ_BASE - 1;
217                         do {
218                                 i = ffs(mask);
219                                 cirq += i;
220                                 mask >>= i;
221                                 irq_set_chip(cirq, &gpioirqs);
222                                 irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
223                         } while (mask);
224                         irq_set_handler_data(irq, demux_masks + n);
225                         irq_set_chained_handler(irq, demux_irqs);
226                         if (++n == ARRAY_SIZE(demux_masks))
227                                 break;
228                 }
229         }
230 }