diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpio/gpio-grgpio.c | 362 |
1 files changed, 359 insertions, 3 deletions
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 466a1c757fae..8e08b8647655 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c | |||
@@ -32,6 +32,9 @@ | |||
32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/err.h> | 33 | #include <linux/err.h> |
34 | #include <linux/basic_mmio_gpio.h> | 34 | #include <linux/basic_mmio_gpio.h> |
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/irq.h> | ||
37 | #include <linux/irqdomain.h> | ||
35 | 38 | ||
36 | #define GRGPIO_MAX_NGPIO 32 | 39 | #define GRGPIO_MAX_NGPIO 32 |
37 | 40 | ||
@@ -44,10 +47,49 @@ | |||
44 | #define GRGPIO_BYPASS 0x18 | 47 | #define GRGPIO_BYPASS 0x18 |
45 | #define GRGPIO_IMAP_BASE 0x20 | 48 | #define GRGPIO_IMAP_BASE 0x20 |
46 | 49 | ||
50 | /* Structure for an irq of the core - called an underlying irq */ | ||
51 | struct grgpio_uirq { | ||
52 | u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */ | ||
53 | u8 uirq; /* Underlying irq of the gpio driver */ | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * Structure for an irq of a gpio line handed out by this driver. The index is | ||
58 | * used to map to the corresponding underlying irq. | ||
59 | */ | ||
60 | struct grgpio_lirq { | ||
61 | s8 index; /* Index into struct grgpio_priv's uirqs, or -1 */ | ||
62 | u8 irq; /* irq for the gpio line */ | ||
63 | }; | ||
64 | |||
47 | struct grgpio_priv { | 65 | struct grgpio_priv { |
48 | struct bgpio_chip bgc; | 66 | struct bgpio_chip bgc; |
49 | void __iomem *regs; | 67 | void __iomem *regs; |
50 | struct device *dev; | 68 | struct device *dev; |
69 | |||
70 | u32 imask; /* irq mask shadow register */ | ||
71 | |||
72 | /* | ||
73 | * The grgpio core can have multiple "underlying" irqs. The gpio lines | ||
74 | * can be mapped to any one or none of these underlying irqs | ||
75 | * independently of each other. This driver sets up an irq domain and | ||
76 | * hands out separate irqs to each gpio line | ||
77 | */ | ||
78 | struct irq_domain *domain; | ||
79 | |||
80 | /* | ||
81 | * This array contains information on each underlying irq, each | ||
82 | * irq of the grgpio core itself. | ||
83 | */ | ||
84 | struct grgpio_uirq uirqs[GRGPIO_MAX_NGPIO]; | ||
85 | |||
86 | /* | ||
87 | * This array contains information for each gpio line on the irqs | ||
88 | * obtains from this driver. An index value of -1 for a certain gpio | ||
89 | * line indicates that the line has no irq. Otherwise the index connects | ||
90 | * the irq to the underlying irq by pointing into the uirqs array. | ||
91 | */ | ||
92 | struct grgpio_lirq lirqs[GRGPIO_MAX_NGPIO]; | ||
51 | }; | 93 | }; |
52 | 94 | ||
53 | static inline struct grgpio_priv *grgpio_gc_to_priv(struct gpio_chip *gc) | 95 | static inline struct grgpio_priv *grgpio_gc_to_priv(struct gpio_chip *gc) |
@@ -57,6 +99,246 @@ static inline struct grgpio_priv *grgpio_gc_to_priv(struct gpio_chip *gc) | |||
57 | return container_of(bgc, struct grgpio_priv, bgc); | 99 | return container_of(bgc, struct grgpio_priv, bgc); |
58 | } | 100 | } |
59 | 101 | ||
102 | static void grgpio_set_imask(struct grgpio_priv *priv, unsigned int offset, | ||
103 | int val) | ||
104 | { | ||
105 | struct bgpio_chip *bgc = &priv->bgc; | ||
106 | unsigned long mask = bgc->pin2mask(bgc, offset); | ||
107 | unsigned long flags; | ||
108 | |||
109 | spin_lock_irqsave(&bgc->lock, flags); | ||
110 | |||
111 | if (val) | ||
112 | priv->imask |= mask; | ||
113 | else | ||
114 | priv->imask &= ~mask; | ||
115 | bgc->write_reg(priv->regs + GRGPIO_IMASK, priv->imask); | ||
116 | |||
117 | spin_unlock_irqrestore(&bgc->lock, flags); | ||
118 | } | ||
119 | |||
120 | static int grgpio_to_irq(struct gpio_chip *gc, unsigned offset) | ||
121 | { | ||
122 | struct grgpio_priv *priv = grgpio_gc_to_priv(gc); | ||
123 | |||
124 | if (offset > gc->ngpio) | ||
125 | return -ENXIO; | ||
126 | |||
127 | if (priv->lirqs[offset].index < 0) | ||
128 | return -ENXIO; | ||
129 | |||
130 | return irq_create_mapping(priv->domain, offset); | ||
131 | } | ||
132 | |||
133 | /* -------------------- IRQ chip functions -------------------- */ | ||
134 | |||
135 | static int grgpio_irq_set_type(struct irq_data *d, unsigned int type) | ||
136 | { | ||
137 | struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); | ||
138 | unsigned long flags; | ||
139 | u32 mask = BIT(d->hwirq); | ||
140 | u32 ipol; | ||
141 | u32 iedge; | ||
142 | u32 pol; | ||
143 | u32 edge; | ||
144 | |||
145 | switch (type) { | ||
146 | case IRQ_TYPE_LEVEL_LOW: | ||
147 | pol = 0; | ||
148 | edge = 0; | ||
149 | break; | ||
150 | case IRQ_TYPE_LEVEL_HIGH: | ||
151 | pol = mask; | ||
152 | edge = 0; | ||
153 | break; | ||
154 | case IRQ_TYPE_EDGE_FALLING: | ||
155 | pol = 0; | ||
156 | edge = mask; | ||
157 | break; | ||
158 | case IRQ_TYPE_EDGE_RISING: | ||
159 | pol = mask; | ||
160 | edge = mask; | ||
161 | break; | ||
162 | default: | ||
163 | return -EINVAL; | ||
164 | } | ||
165 | |||
166 | spin_lock_irqsave(&priv->bgc.lock, flags); | ||
167 | |||
168 | ipol = priv->bgc.read_reg(priv->regs + GRGPIO_IPOL) & ~mask; | ||
169 | iedge = priv->bgc.read_reg(priv->regs + GRGPIO_IEDGE) & ~mask; | ||
170 | |||
171 | priv->bgc.write_reg(priv->regs + GRGPIO_IPOL, ipol | pol); | ||
172 | priv->bgc.write_reg(priv->regs + GRGPIO_IEDGE, iedge | edge); | ||
173 | |||
174 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static void grgpio_irq_mask(struct irq_data *d) | ||
180 | { | ||
181 | struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); | ||
182 | int offset = d->hwirq; | ||
183 | |||
184 | grgpio_set_imask(priv, offset, 0); | ||
185 | } | ||
186 | |||
187 | static void grgpio_irq_unmask(struct irq_data *d) | ||
188 | { | ||
189 | struct grgpio_priv *priv = irq_data_get_irq_chip_data(d); | ||
190 | int offset = d->hwirq; | ||
191 | |||
192 | grgpio_set_imask(priv, offset, 1); | ||
193 | } | ||
194 | |||
195 | static struct irq_chip grgpio_irq_chip = { | ||
196 | .name = "grgpio", | ||
197 | .irq_mask = grgpio_irq_mask, | ||
198 | .irq_unmask = grgpio_irq_unmask, | ||
199 | .irq_set_type = grgpio_irq_set_type, | ||
200 | }; | ||
201 | |||
202 | static irqreturn_t grgpio_irq_handler(int irq, void *dev) | ||
203 | { | ||
204 | struct grgpio_priv *priv = dev; | ||
205 | int ngpio = priv->bgc.gc.ngpio; | ||
206 | unsigned long flags; | ||
207 | int i; | ||
208 | int match = 0; | ||
209 | |||
210 | spin_lock_irqsave(&priv->bgc.lock, flags); | ||
211 | |||
212 | /* | ||
213 | * For each gpio line, call its interrupt handler if it its underlying | ||
214 | * irq matches the current irq that is handled. | ||
215 | */ | ||
216 | for (i = 0; i < ngpio; i++) { | ||
217 | struct grgpio_lirq *lirq = &priv->lirqs[i]; | ||
218 | |||
219 | if (priv->imask & BIT(i) && lirq->index >= 0 && | ||
220 | priv->uirqs[lirq->index].uirq == irq) { | ||
221 | generic_handle_irq(lirq->irq); | ||
222 | match = 1; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
227 | |||
228 | if (!match) | ||
229 | dev_warn(priv->dev, "No gpio line matched irq %d\n", irq); | ||
230 | |||
231 | return IRQ_HANDLED; | ||
232 | } | ||
233 | |||
234 | /* | ||
235 | * This function will be called as a consequence of the call to | ||
236 | * irq_create_mapping in grgpio_to_irq | ||
237 | */ | ||
238 | int grgpio_irq_map(struct irq_domain *d, unsigned int irq, | ||
239 | irq_hw_number_t hwirq) | ||
240 | { | ||
241 | struct grgpio_priv *priv = d->host_data; | ||
242 | struct grgpio_lirq *lirq; | ||
243 | struct grgpio_uirq *uirq; | ||
244 | unsigned long flags; | ||
245 | int offset = hwirq; | ||
246 | int ret = 0; | ||
247 | |||
248 | if (!priv) | ||
249 | return -EINVAL; | ||
250 | |||
251 | lirq = &priv->lirqs[offset]; | ||
252 | if (lirq->index < 0) | ||
253 | return -EINVAL; | ||
254 | |||
255 | dev_dbg(priv->dev, "Mapping irq %d for gpio line %d\n", | ||
256 | irq, offset); | ||
257 | |||
258 | spin_lock_irqsave(&priv->bgc.lock, flags); | ||
259 | |||
260 | /* Request underlying irq if not already requested */ | ||
261 | lirq->irq = irq; | ||
262 | uirq = &priv->uirqs[lirq->index]; | ||
263 | if (uirq->refcnt == 0) { | ||
264 | ret = request_irq(uirq->uirq, grgpio_irq_handler, 0, | ||
265 | dev_name(priv->dev), priv); | ||
266 | if (ret) { | ||
267 | dev_err(priv->dev, | ||
268 | "Could not request underlying irq %d\n", | ||
269 | uirq->uirq); | ||
270 | |||
271 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
272 | |||
273 | return ret; | ||
274 | } | ||
275 | } | ||
276 | uirq->refcnt++; | ||
277 | |||
278 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
279 | |||
280 | /* Setup irq */ | ||
281 | irq_set_chip_data(irq, priv); | ||
282 | irq_set_chip_and_handler(irq, &grgpio_irq_chip, | ||
283 | handle_simple_irq); | ||
284 | irq_clear_status_flags(irq, IRQ_NOREQUEST); | ||
285 | #ifdef CONFIG_ARM | ||
286 | set_irq_flags(irq, IRQF_VALID); | ||
287 | #else | ||
288 | irq_set_noprobe(irq); | ||
289 | #endif | ||
290 | |||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq) | ||
295 | { | ||
296 | struct grgpio_priv *priv = d->host_data; | ||
297 | int index; | ||
298 | struct grgpio_lirq *lirq; | ||
299 | struct grgpio_uirq *uirq; | ||
300 | unsigned long flags; | ||
301 | int ngpio = priv->bgc.gc.ngpio; | ||
302 | int i; | ||
303 | |||
304 | #ifdef CONFIG_ARM | ||
305 | set_irq_flags(irq, 0); | ||
306 | #endif | ||
307 | irq_set_chip_and_handler(irq, NULL, NULL); | ||
308 | irq_set_chip_data(irq, NULL); | ||
309 | |||
310 | spin_lock_irqsave(&priv->bgc.lock, flags); | ||
311 | |||
312 | /* Free underlying irq if last user unmapped */ | ||
313 | index = -1; | ||
314 | for (i = 0; i < ngpio; i++) { | ||
315 | lirq = &priv->lirqs[i]; | ||
316 | if (lirq->irq == irq) { | ||
317 | grgpio_set_imask(priv, i, 0); | ||
318 | lirq->irq = 0; | ||
319 | index = lirq->index; | ||
320 | break; | ||
321 | } | ||
322 | } | ||
323 | WARN_ON(index < 0); | ||
324 | |||
325 | if (index >= 0) { | ||
326 | uirq = &priv->uirqs[lirq->index]; | ||
327 | uirq->refcnt--; | ||
328 | if (uirq->refcnt == 0) | ||
329 | free_irq(uirq->uirq, priv); | ||
330 | } | ||
331 | |||
332 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
333 | } | ||
334 | |||
335 | static struct irq_domain_ops grgpio_irq_domain_ops = { | ||
336 | .map = grgpio_irq_map, | ||
337 | .unmap = grgpio_irq_unmap, | ||
338 | }; | ||
339 | |||
340 | /* ------------------------------------------------------------ */ | ||
341 | |||
60 | static int grgpio_probe(struct platform_device *ofdev) | 342 | static int grgpio_probe(struct platform_device *ofdev) |
61 | { | 343 | { |
62 | struct device_node *np = ofdev->dev.of_node; | 344 | struct device_node *np = ofdev->dev.of_node; |
@@ -67,6 +349,9 @@ static int grgpio_probe(struct platform_device *ofdev) | |||
67 | struct resource *res; | 349 | struct resource *res; |
68 | int err; | 350 | int err; |
69 | u32 prop; | 351 | u32 prop; |
352 | s32 *irqmap; | ||
353 | int size; | ||
354 | int i; | ||
70 | 355 | ||
71 | priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); | 356 | priv = devm_kzalloc(&ofdev->dev, sizeof(*priv), GFP_KERNEL); |
72 | if (!priv) | 357 | if (!priv) |
@@ -87,11 +372,13 @@ static int grgpio_probe(struct platform_device *ofdev) | |||
87 | } | 372 | } |
88 | 373 | ||
89 | priv->regs = regs; | 374 | priv->regs = regs; |
375 | priv->imask = bgc->read_reg(regs + GRGPIO_IMASK); | ||
90 | priv->dev = &ofdev->dev; | 376 | priv->dev = &ofdev->dev; |
91 | 377 | ||
92 | gc = &bgc->gc; | 378 | gc = &bgc->gc; |
93 | gc->of_node = np; | 379 | gc->of_node = np; |
94 | gc->owner = THIS_MODULE; | 380 | gc->owner = THIS_MODULE; |
381 | gc->to_irq = grgpio_to_irq; | ||
95 | gc->label = np->full_name; | 382 | gc->label = np->full_name; |
96 | gc->base = -1; | 383 | gc->base = -1; |
97 | 384 | ||
@@ -104,6 +391,51 @@ static int grgpio_probe(struct platform_device *ofdev) | |||
104 | gc->ngpio = prop; | 391 | gc->ngpio = prop; |
105 | } | 392 | } |
106 | 393 | ||
394 | /* | ||
395 | * The irqmap contains the index values indicating which underlying irq, | ||
396 | * if anyone, is connected to that line | ||
397 | */ | ||
398 | irqmap = (s32 *)of_get_property(np, "irqmap", &size); | ||
399 | if (irqmap) { | ||
400 | if (size < gc->ngpio) { | ||
401 | dev_err(&ofdev->dev, | ||
402 | "irqmap shorter than ngpio (%d < %d)\n", | ||
403 | size, gc->ngpio); | ||
404 | return -EINVAL; | ||
405 | } | ||
406 | |||
407 | priv->domain = irq_domain_add_linear(np, gc->ngpio, | ||
408 | &grgpio_irq_domain_ops, | ||
409 | priv); | ||
410 | if (!priv->domain) { | ||
411 | dev_err(&ofdev->dev, "Could not add irq domain\n"); | ||
412 | return -EINVAL; | ||
413 | } | ||
414 | |||
415 | for (i = 0; i < gc->ngpio; i++) { | ||
416 | struct grgpio_lirq *lirq; | ||
417 | int ret; | ||
418 | |||
419 | lirq = &priv->lirqs[i]; | ||
420 | lirq->index = irqmap[i]; | ||
421 | |||
422 | if (lirq->index < 0) | ||
423 | continue; | ||
424 | |||
425 | ret = platform_get_irq(ofdev, lirq->index); | ||
426 | if (ret <= 0) { | ||
427 | /* | ||
428 | * Continue without irq functionality for that | ||
429 | * gpio line | ||
430 | */ | ||
431 | dev_err(priv->dev, | ||
432 | "Failed to get irq for offset %d\n", i); | ||
433 | continue; | ||
434 | } | ||
435 | priv->uirqs[lirq->index].uirq = ret; | ||
436 | } | ||
437 | } | ||
438 | |||
107 | platform_set_drvdata(ofdev, priv); | 439 | platform_set_drvdata(ofdev, priv); |
108 | 440 | ||
109 | err = gpiochip_add(gc); | 441 | err = gpiochip_add(gc); |
@@ -112,8 +444,8 @@ static int grgpio_probe(struct platform_device *ofdev) | |||
112 | return err; | 444 | return err; |
113 | } | 445 | } |
114 | 446 | ||
115 | dev_info(&ofdev->dev, "regs=0x%p, base=%d, ngpio=%d\n", | 447 | dev_info(&ofdev->dev, "regs=0x%p, base=%d, ngpio=%d, irqs=%s\n", |
116 | priv->regs, gc->base, gc->ngpio); | 448 | priv->regs, gc->base, gc->ngpio, priv->domain ? "on" : "off"); |
117 | 449 | ||
118 | return 0; | 450 | return 0; |
119 | } | 451 | } |
@@ -121,8 +453,32 @@ static int grgpio_probe(struct platform_device *ofdev) | |||
121 | static int grgpio_remove(struct platform_device *ofdev) | 453 | static int grgpio_remove(struct platform_device *ofdev) |
122 | { | 454 | { |
123 | struct grgpio_priv *priv = platform_get_drvdata(ofdev); | 455 | struct grgpio_priv *priv = platform_get_drvdata(ofdev); |
456 | unsigned long flags; | ||
457 | int i; | ||
458 | int ret = 0; | ||
459 | |||
460 | spin_lock_irqsave(&priv->bgc.lock, flags); | ||
461 | |||
462 | if (priv->domain) { | ||
463 | for (i = 0; i < GRGPIO_MAX_NGPIO; i++) { | ||
464 | if (priv->uirqs[i].refcnt != 0) { | ||
465 | ret = -EBUSY; | ||
466 | goto out; | ||
467 | } | ||
468 | } | ||
469 | } | ||
470 | |||
471 | ret = gpiochip_remove(&priv->bgc.gc); | ||
472 | if (ret) | ||
473 | goto out; | ||
474 | |||
475 | if (priv->domain) | ||
476 | irq_domain_remove(priv->domain); | ||
477 | |||
478 | out: | ||
479 | spin_unlock_irqrestore(&priv->bgc.lock, flags); | ||
124 | 480 | ||
125 | return gpiochip_remove(&priv->bgc.gc); | 481 | return ret; |
126 | } | 482 | } |
127 | 483 | ||
128 | static struct of_device_id grgpio_match[] = { | 484 | static struct of_device_id grgpio_match[] = { |