Commit 0b3eb21b2f2222c4c1e3e21fc3cd427404d3991a
Committed by
Chris Zankel
1 parent
f24e552c2d
Exists in
master
and in
39 other branches
xtensa: support s6000 gpio irqs and alternate function selection
Implement an irq chip to handle interrupts via gpio. The GPIO chip initialization function now takes a bitmask denoting pins that should be configured for their alternate function. changes compared to v1: - fixed bug on edge interrupt configuration - accommodated to function name change - moved definition of VARIANT_NR_IRQS to this patch - renamed __XTENSA_S6000_IRQ_H to _XTENSA_S6000_IRQ_H as requested Signed-off-by: Daniel Glöckner <dg@emlix.com> Signed-off-by: Johannes Weiner <jw@emlix.com> Signed-off-by: Chris Zankel <chris@zankel.net>
Showing 5 changed files with 171 additions and 10 deletions Side-by-side Diff
arch/xtensa/include/asm/gpio.h
... | ... | @@ -38,14 +38,14 @@ |
38 | 38 | return __gpio_cansleep(gpio); |
39 | 39 | } |
40 | 40 | |
41 | -/* | |
42 | - * Not implemented, yet. | |
43 | - */ | |
44 | 41 | static inline int gpio_to_irq(unsigned int gpio) |
45 | 42 | { |
46 | - return -ENOSYS; | |
43 | + return __gpio_to_irq(gpio); | |
47 | 44 | } |
48 | 45 | |
46 | +/* | |
47 | + * Not implemented, yet. | |
48 | + */ | |
49 | 49 | static inline int irq_to_gpio(unsigned int irq) |
50 | 50 | { |
51 | 51 | return -EINVAL; |
arch/xtensa/platforms/s6105/setup.c
arch/xtensa/variants/s6000/gpio.c
... | ... | @@ -4,15 +4,20 @@ |
4 | 4 | * Copyright (c) 2009 emlix GmbH |
5 | 5 | * Authors: Oskar Schirmer <os@emlix.com> |
6 | 6 | * Johannes Weiner <jw@emlix.com> |
7 | + * Daniel Gloeckner <dg@emlix.com> | |
7 | 8 | */ |
9 | +#include <linux/bitops.h> | |
8 | 10 | #include <linux/kernel.h> |
9 | 11 | #include <linux/module.h> |
10 | 12 | #include <linux/init.h> |
11 | 13 | #include <linux/io.h> |
14 | +#include <linux/irq.h> | |
12 | 15 | #include <linux/gpio.h> |
13 | 16 | |
14 | 17 | #include <variant/hardware.h> |
15 | 18 | |
19 | +#define IRQ_BASE XTENSA_NR_IRQS | |
20 | + | |
16 | 21 | #define S6_GPIO_DATA 0x000 |
17 | 22 | #define S6_GPIO_IS 0x404 |
18 | 23 | #define S6_GPIO_IBE 0x408 |
19 | 24 | |
20 | 25 | |
21 | 26 | |
22 | 27 | |
... | ... | @@ -52,20 +57,176 @@ |
52 | 57 | writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off)); |
53 | 58 | } |
54 | 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 | + | |
55 | 67 | static struct gpio_chip gpiochip = { |
56 | 68 | .owner = THIS_MODULE, |
57 | 69 | .direction_input = direction_input, |
58 | 70 | .get = get, |
59 | 71 | .direction_output = direction_output, |
60 | 72 | .set = set, |
73 | + .to_irq = to_irq, | |
61 | 74 | .base = 0, |
62 | 75 | .ngpio = 24, |
63 | 76 | .can_sleep = 0, /* no blocking io needed */ |
64 | 77 | .exported = 0, /* no exporting to userspace */ |
65 | 78 | }; |
66 | 79 | |
67 | -int s6_gpio_init(void) | |
80 | +int s6_gpio_init(u32 afsel) | |
68 | 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); | |
69 | 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 | + } | |
70 | 231 | } |
arch/xtensa/variants/s6000/include/variant/gpio.h
arch/xtensa/variants/s6000/include/variant/irq.h
1 | -#ifndef __XTENSA_S6000_IRQ_H | |
2 | -#define __XTENSA_S6000_IRQ_H | |
1 | +#ifndef _XTENSA_S6000_IRQ_H | |
2 | +#define _XTENSA_S6000_IRQ_H | |
3 | 3 | |
4 | 4 | #define NO_IRQ (-1) |
5 | +#define VARIANT_NR_IRQS 8 /* GPIO interrupts */ | |
5 | 6 | |
6 | 7 | extern void variant_irq_enable(unsigned int irq); |
7 | -extern void variant_irq_disable(unsigned int irq); | |
8 | 8 | |
9 | 9 | #endif /* __XTENSA_S6000_IRQ_H */ |