blob: da3d1062f8db99e4190c19ea4cf1ddff7a3f9466 [file] [log] [blame]
Sathish Ambleyd2ad0fa2012-03-23 11:23:47 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13#include <linux/bitmap.h>
14#include <linux/bitops.h>
15#include <linux/gpio.h>
16#include <linux/init.h>
17#include <linux/interrupt.h>
18#include <linux/irq.h>
19#include <linux/io.h>
20#include <linux/module.h>
21#include <linux/spinlock.h>
22#include <linux/syscore_ops.h>
23#include <linux/irqdomain.h>
24#include <linux/of.h>
25#include <linux/err.h>
26
27#include <asm/mach/irq.h>
28
29#include <mach/msm_iomap.h>
30#include <mach/gpiomux.h>
31#include <mach/mpm.h>
32#include "gpio-msm-common.h"
33
34enum msm_tlmm_register {
35 SDC4_HDRV_PULL_CTL = 0x20a0,
36 SDC3_HDRV_PULL_CTL = 0x20a4,
37 SDC1_HDRV_PULL_CTL = 0x20a0,
38};
39
40struct tlmm_field_cfg {
41 enum msm_tlmm_register reg;
42 u8 off;
43};
44
45static const struct tlmm_field_cfg tlmm_hdrv_cfgs[] = {
46 {SDC4_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC4_CLK */
47 {SDC4_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC4_CMD */
48 {SDC4_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC4_DATA */
49 {SDC3_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC3_CLK */
50 {SDC3_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC3_CMD */
51 {SDC3_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC3_DATA */
52 {SDC1_HDRV_PULL_CTL, 6}, /* TLMM_HDRV_SDC1_CLK */
53 {SDC1_HDRV_PULL_CTL, 3}, /* TLMM_HDRV_SDC1_CMD */
54 {SDC1_HDRV_PULL_CTL, 0}, /* TLMM_HDRV_SDC1_DATA */
55};
56
57static const struct tlmm_field_cfg tlmm_pull_cfgs[] = {
58 {SDC4_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC4_CMD */
59 {SDC4_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC4_DATA */
60 {SDC3_HDRV_PULL_CTL, 14}, /* TLMM_PULL_SDC3_CLK */
61 {SDC3_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC3_CMD */
62 {SDC3_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC3_DATA */
63 {SDC1_HDRV_PULL_CTL, 13}, /* TLMM_PULL_SDC1_CLK */
64 {SDC1_HDRV_PULL_CTL, 11}, /* TLMM_PULL_SDC1_CMD */
65 {SDC1_HDRV_PULL_CTL, 9}, /* TLMM_PULL_SDC1_DATA */
66};
67
68/*
69 * Supported arch specific irq extension.
70 * Default make them NULL.
71 */
72struct irq_chip msm_gpio_irq_extn = {
73 .irq_eoi = NULL,
74 .irq_mask = NULL,
75 .irq_unmask = NULL,
76 .irq_retrigger = NULL,
77 .irq_set_type = NULL,
78 .irq_set_wake = NULL,
79 .irq_disable = NULL,
80};
81
82/**
83 * struct msm_gpio_dev: the MSM8660 SoC GPIO device structure
84 *
85 * @enabled_irqs: a bitmap used to optimize the summary-irq handler. By
86 * keeping track of which gpios are unmasked as irq sources, we avoid
87 * having to do __raw_readl calls on hundreds of iomapped registers each time
88 * the summary interrupt fires in order to locate the active interrupts.
89 *
90 * @wake_irqs: a bitmap for tracking which interrupt lines are enabled
91 * as wakeup sources. When the device is suspended, interrupts which are
92 * not wakeup sources are disabled.
93 *
94 * @dual_edge_irqs: a bitmap used to track which irqs are configured
95 * as dual-edge, as this is not supported by the hardware and requires
96 * some special handling in the driver.
97 */
98struct msm_gpio_dev {
99 struct gpio_chip gpio_chip;
100 DECLARE_BITMAP(enabled_irqs, NR_MSM_GPIOS);
101 DECLARE_BITMAP(wake_irqs, NR_MSM_GPIOS);
102 DECLARE_BITMAP(dual_edge_irqs, NR_MSM_GPIOS);
103 struct irq_domain domain;
104};
105
106static DEFINE_SPINLOCK(tlmm_lock);
107
108static inline struct msm_gpio_dev *to_msm_gpio_dev(struct gpio_chip *chip)
109{
110 return container_of(chip, struct msm_gpio_dev, gpio_chip);
111}
112
113static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
114{
115 int rc;
116 rc = __msm_gpio_get_inout(offset);
117 mb();
118 return rc;
119}
120
121static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
122{
123 __msm_gpio_set_inout(offset, val);
124 mb();
125}
126
127static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
128{
129 unsigned long irq_flags;
130
131 spin_lock_irqsave(&tlmm_lock, irq_flags);
132 __msm_gpio_set_config_direction(offset, 1, 0);
133 mb();
134 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
135 return 0;
136}
137
138static int msm_gpio_direction_output(struct gpio_chip *chip,
139 unsigned offset,
140 int val)
141{
142 unsigned long irq_flags;
143
144 spin_lock_irqsave(&tlmm_lock, irq_flags);
145 __msm_gpio_set_config_direction(offset, 0, val);
146 mb();
147 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
148 return 0;
149}
150
151#ifdef CONFIG_OF
152static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
153{
154 struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
155 struct irq_domain *domain = &g_dev->domain;
156 return domain->irq_base + (offset - chip->base);
157}
158
159static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
160{
161 struct msm_gpio_dev *g_dev = to_msm_gpio_dev(chip);
162 struct irq_domain *domain = &g_dev->domain;
163 return irq - domain->irq_base;
164}
165#else
166static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
167{
168 return MSM_GPIO_TO_INT(offset - chip->base);
169}
170
171static inline int msm_irq_to_gpio(struct gpio_chip *chip, unsigned irq)
172{
173 return irq - MSM_GPIO_TO_INT(chip->base);
174}
175#endif
176
177static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
178{
179 return msm_gpiomux_get(chip->base + offset);
180}
181
182static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
183{
184 msm_gpiomux_put(chip->base + offset);
185}
186
187static struct msm_gpio_dev msm_gpio = {
188 .gpio_chip = {
189 .label = "msmgpio",
190 .base = 0,
191 .ngpio = NR_MSM_GPIOS,
192 .direction_input = msm_gpio_direction_input,
193 .direction_output = msm_gpio_direction_output,
194 .get = msm_gpio_get,
195 .set = msm_gpio_set,
196 .to_irq = msm_gpio_to_irq,
197 .request = msm_gpio_request,
198 .free = msm_gpio_free,
199 },
200};
201
202static void switch_mpm_config(struct irq_data *d, unsigned val)
203{
204 /* switch the configuration in the mpm as well */
205 if (!msm_gpio_irq_extn.irq_set_type)
206 return;
207
208 if (val)
209 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_FALLING);
210 else
211 msm_gpio_irq_extn.irq_set_type(d, IRQF_TRIGGER_RISING);
212}
213
214/* For dual-edge interrupts in software, since the hardware has no
215 * such support:
216 *
217 * At appropriate moments, this function may be called to flip the polarity
218 * settings of both-edge irq lines to try and catch the next edge.
219 *
220 * The attempt is considered successful if:
221 * - the status bit goes high, indicating that an edge was caught, or
222 * - the input value of the gpio doesn't change during the attempt.
223 * If the value changes twice during the process, that would cause the first
224 * test to fail but would force the second, as two opposite
225 * transitions would cause a detection no matter the polarity setting.
226 *
227 * The do-loop tries to sledge-hammer closed the timing hole between
228 * the initial value-read and the polarity-write - if the line value changes
229 * during that window, an interrupt is lost, the new polarity setting is
230 * incorrect, and the first success test will fail, causing a retry.
231 *
232 * Algorithm comes from Google's msmgpio driver, see mach-msm/gpio.c.
233 */
234static void msm_gpio_update_dual_edge_pos(struct irq_data *d, unsigned gpio)
235{
236 int loop_limit = 100;
237 unsigned val, val2, intstat;
238
239 do {
240 val = __msm_gpio_get_inout(gpio);
241 __msm_gpio_set_polarity(gpio, val);
242 val2 = __msm_gpio_get_inout(gpio);
243 intstat = __msm_gpio_get_intr_status(gpio);
244 if (intstat || val == val2) {
245 switch_mpm_config(d, val);
246 return;
247 }
248 } while (loop_limit-- > 0);
249 pr_err("%s: dual-edge irq failed to stabilize, %#08x != %#08x\n",
250 __func__, val, val2);
251}
252
253static void msm_gpio_irq_ack(struct irq_data *d)
254{
255 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
256
257 __msm_gpio_set_intr_status(gpio);
258 if (test_bit(gpio, msm_gpio.dual_edge_irqs))
259 msm_gpio_update_dual_edge_pos(d, gpio);
260 mb();
261}
262
263static void msm_gpio_irq_mask(struct irq_data *d)
264{
265 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
266 unsigned long irq_flags;
267
268 spin_lock_irqsave(&tlmm_lock, irq_flags);
269 __msm_gpio_set_intr_cfg_enable(gpio, 0);
270 __clear_bit(gpio, msm_gpio.enabled_irqs);
271 mb();
272 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
273
274 if (msm_gpio_irq_extn.irq_mask)
275 msm_gpio_irq_extn.irq_mask(d);
276
277}
278
279static void msm_gpio_irq_unmask(struct irq_data *d)
280{
281 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
282 unsigned long irq_flags;
283
284 spin_lock_irqsave(&tlmm_lock, irq_flags);
285 __set_bit(gpio, msm_gpio.enabled_irqs);
286 __msm_gpio_set_intr_cfg_enable(gpio, 1);
287 mb();
288 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
289
290 if (msm_gpio_irq_extn.irq_mask)
291 msm_gpio_irq_extn.irq_unmask(d);
292}
293
294static void msm_gpio_irq_disable(struct irq_data *d)
295{
296 if (msm_gpio_irq_extn.irq_disable)
297 msm_gpio_irq_extn.irq_disable(d);
298}
299
300static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
301{
302 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
303 unsigned long irq_flags;
304
305 spin_lock_irqsave(&tlmm_lock, irq_flags);
306
307 if (flow_type & IRQ_TYPE_EDGE_BOTH) {
308 __irq_set_handler_locked(d->irq, handle_edge_irq);
309 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
310 __set_bit(gpio, msm_gpio.dual_edge_irqs);
311 else
312 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
313 } else {
314 __irq_set_handler_locked(d->irq, handle_level_irq);
315 __clear_bit(gpio, msm_gpio.dual_edge_irqs);
316 }
317
318 __msm_gpio_set_intr_cfg_type(gpio, flow_type);
319
320 if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
321 msm_gpio_update_dual_edge_pos(d, gpio);
322
323 mb();
324 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
325
326 if (msm_gpio_irq_extn.irq_set_type)
327 msm_gpio_irq_extn.irq_set_type(d, flow_type);
328
329 return 0;
330}
331
332/*
333 * When the summary IRQ is raised, any number of GPIO lines may be high.
334 * It is the job of the summary handler to find all those GPIO lines
335 * which have been set as summary IRQ lines and which are triggered,
336 * and to call their interrupt handlers.
337 */
338static irqreturn_t msm_summary_irq_handler(int irq, void *data)
339{
340 unsigned long i;
341 struct irq_desc *desc = irq_to_desc(irq);
342 struct irq_chip *chip = irq_desc_get_chip(desc);
343
344 chained_irq_enter(chip, desc);
345
346 for (i = find_first_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
347 i < NR_MSM_GPIOS;
348 i = find_next_bit(msm_gpio.enabled_irqs, NR_MSM_GPIOS, i + 1)) {
349 if (__msm_gpio_get_intr_status(i))
350 generic_handle_irq(msm_gpio_to_irq(&msm_gpio.gpio_chip,
351 i));
352 }
353
354 chained_irq_exit(chip, desc);
355 return IRQ_HANDLED;
356}
357
358static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
359{
360 int gpio = msm_irq_to_gpio(&msm_gpio.gpio_chip, d->irq);
361
362 if (on) {
363 if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
364 irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 1);
365 set_bit(gpio, msm_gpio.wake_irqs);
366 } else {
367 clear_bit(gpio, msm_gpio.wake_irqs);
368 if (bitmap_empty(msm_gpio.wake_irqs, NR_MSM_GPIOS))
369 irq_set_irq_wake(TLMM_MSM_SUMMARY_IRQ, 0);
370 }
371
372 if (msm_gpio_irq_extn.irq_set_wake)
373 msm_gpio_irq_extn.irq_set_wake(d, on);
374
375 return 0;
376}
377
378static struct irq_chip msm_gpio_irq_chip = {
379 .name = "msmgpio",
380 .irq_mask = msm_gpio_irq_mask,
381 .irq_unmask = msm_gpio_irq_unmask,
382 .irq_ack = msm_gpio_irq_ack,
383 .irq_set_type = msm_gpio_irq_set_type,
384 .irq_set_wake = msm_gpio_irq_set_wake,
385 .irq_disable = msm_gpio_irq_disable,
386};
387
388/*
389 * This lock class tells lockdep that GPIO irqs are in a different
390 * category than their parent, so it won't report false recursion.
391 */
392static struct lock_class_key msm_gpio_lock_class;
393
394static int __devinit msm_gpio_probe(void)
395{
396 int i, irq, ret;
397
398 spin_lock_init(&tlmm_lock);
399 bitmap_zero(msm_gpio.enabled_irqs, NR_MSM_GPIOS);
400 bitmap_zero(msm_gpio.wake_irqs, NR_MSM_GPIOS);
401 bitmap_zero(msm_gpio.dual_edge_irqs, NR_MSM_GPIOS);
402 ret = gpiochip_add(&msm_gpio.gpio_chip);
403 if (ret < 0)
404 return ret;
405
406 for (i = 0; i < msm_gpio.gpio_chip.ngpio; ++i) {
407 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
408 irq_set_lockdep_class(irq, &msm_gpio_lock_class);
409 irq_set_chip_and_handler(irq, &msm_gpio_irq_chip,
410 handle_level_irq);
411 set_irq_flags(irq, IRQF_VALID);
412 }
413
414 ret = request_irq(TLMM_MSM_SUMMARY_IRQ, msm_summary_irq_handler,
415 IRQF_TRIGGER_HIGH, "msmgpio", NULL);
416 if (ret) {
417 pr_err("Request_irq failed for TLMM_MSM_SUMMARY_IRQ - %d\n",
418 ret);
419 return ret;
420 }
421 return 0;
422}
423
424static int __devexit msm_gpio_remove(void)
425{
426 int ret = gpiochip_remove(&msm_gpio.gpio_chip);
427
428 if (ret < 0)
429 return ret;
430
431 irq_set_handler(TLMM_MSM_SUMMARY_IRQ, NULL);
432
433 return 0;
434}
435
436#ifdef CONFIG_PM
437static int msm_gpio_suspend(void)
438{
439 unsigned long irq_flags;
440 unsigned long i;
441
442 spin_lock_irqsave(&tlmm_lock, irq_flags);
443 for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
444 __msm_gpio_set_intr_cfg_enable(i, 0);
445
446 for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
447 __msm_gpio_set_intr_cfg_enable(i, 1);
448 mb();
449 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
450 return 0;
451}
452
453void msm_gpio_show_resume_irq(void)
454{
455 unsigned long irq_flags;
456 int i, irq, intstat;
457
458 if (!msm_show_resume_irq_mask)
459 return;
460
461 spin_lock_irqsave(&tlmm_lock, irq_flags);
462 for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS) {
463 intstat = __msm_gpio_get_intr_status(i);
464 if (intstat) {
465 irq = msm_gpio_to_irq(&msm_gpio.gpio_chip, i);
466 pr_warning("%s: %d triggered\n",
467 __func__, irq);
468 }
469 }
470 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
471}
472
473static void msm_gpio_resume(void)
474{
475 unsigned long irq_flags;
476 unsigned long i;
477
478 msm_gpio_show_resume_irq();
479
480 spin_lock_irqsave(&tlmm_lock, irq_flags);
481 for_each_set_bit(i, msm_gpio.wake_irqs, NR_MSM_GPIOS)
482 __msm_gpio_set_intr_cfg_enable(i, 0);
483
484 for_each_set_bit(i, msm_gpio.enabled_irqs, NR_MSM_GPIOS)
485 __msm_gpio_set_intr_cfg_enable(i, 1);
486 mb();
487 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
488}
489#else
490#define msm_gpio_suspend NULL
491#define msm_gpio_resume NULL
492#endif
493
494static struct syscore_ops msm_gpio_syscore_ops = {
495 .suspend = msm_gpio_suspend,
496 .resume = msm_gpio_resume,
497};
498
499static int __init msm_gpio_init(void)
500{
501 msm_gpio_probe();
502 register_syscore_ops(&msm_gpio_syscore_ops);
503 return 0;
504}
505
506static void __exit msm_gpio_exit(void)
507{
508 unregister_syscore_ops(&msm_gpio_syscore_ops);
509 msm_gpio_remove();
510}
511
512postcore_initcall(msm_gpio_init);
513module_exit(msm_gpio_exit);
514
515static void msm_tlmm_set_field(const struct tlmm_field_cfg *configs,
516 unsigned id, unsigned width, unsigned val)
517{
518 unsigned long irqflags;
519 u32 mask = (1 << width) - 1;
520 u32 __iomem *reg = MSM_TLMM_BASE + configs[id].reg;
521 u32 reg_val;
522
523 spin_lock_irqsave(&tlmm_lock, irqflags);
524 reg_val = __raw_readl(reg);
525 reg_val &= ~(mask << configs[id].off);
526 reg_val |= (val & mask) << configs[id].off;
527 __raw_writel(reg_val, reg);
528 mb();
529 spin_unlock_irqrestore(&tlmm_lock, irqflags);
530}
531
532void msm_tlmm_set_hdrive(enum msm_tlmm_hdrive_tgt tgt, int drv_str)
533{
534 msm_tlmm_set_field(tlmm_hdrv_cfgs, tgt, 3, drv_str);
535}
536EXPORT_SYMBOL(msm_tlmm_set_hdrive);
537
538void msm_tlmm_set_pull(enum msm_tlmm_pull_tgt tgt, int pull)
539{
540 msm_tlmm_set_field(tlmm_pull_cfgs, tgt, 2, pull);
541}
542EXPORT_SYMBOL(msm_tlmm_set_pull);
543
544int gpio_tlmm_config(unsigned config, unsigned disable)
545{
546 unsigned gpio = GPIO_PIN(config);
547
548 if (gpio > NR_MSM_GPIOS)
549 return -EINVAL;
550
551 __gpio_tlmm_config(config);
552 mb();
553
554 return 0;
555}
556EXPORT_SYMBOL(gpio_tlmm_config);
557
558int msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
559 unsigned int input_polarity)
560{
561 unsigned long irq_flags;
562
563 if (gpio >= NR_MSM_GPIOS || irq >= NR_TLMM_MSM_DIR_CONN_IRQ)
564 return -EINVAL;
565
566 spin_lock_irqsave(&tlmm_lock, irq_flags);
567 __msm_gpio_install_direct_irq(gpio, irq, input_polarity);
568 mb();
569 spin_unlock_irqrestore(&tlmm_lock, irq_flags);
570
571 return 0;
572}
573EXPORT_SYMBOL(msm_gpio_install_direct_irq);
574
575#ifdef CONFIG_OF
576static int msm_gpio_domain_dt_translate(struct irq_domain *d,
577 struct device_node *controller,
578 const u32 *intspec,
579 unsigned int intsize,
580 unsigned long *out_hwirq,
581 unsigned int *out_type)
582{
583 if (d->of_node != controller)
584 return -EINVAL;
585 if (intsize != 2)
586 return -EINVAL;
587
588 /* hwirq value */
589 *out_hwirq = intspec[0];
590
591 /* irq flags */
592 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
593 return 0;
594}
595
596static struct irq_domain_ops msm_gpio_irq_domain_ops = {
597 .dt_translate = msm_gpio_domain_dt_translate,
598};
599
600int __init msm_gpio_of_init(struct device_node *node,
601 struct device_node *parent)
602{
603 struct irq_domain *domain = &msm_gpio.domain;
604 int start;
605
606 start = irq_domain_find_free_range(0, NR_MSM_GPIOS);
607 domain->irq_base = irq_alloc_descs(start, 0, NR_MSM_GPIOS,
608 numa_node_id());
609 if (IS_ERR_VALUE(domain->irq_base)) {
610 WARN(1, "Cannot allocate irq_descs @ IRQ%d\n", start);
611 return domain->irq_base;
612 }
613
614 domain->irq_base = irq_domain_find_free_range(0, NR_MSM_GPIOS);
615 domain->nr_irq = NR_MSM_GPIOS;
616 domain->of_node = of_node_get(node);
617 domain->priv = &msm_gpio;
618 domain->ops = &msm_gpio_irq_domain_ops;
619 irq_domain_add(domain);
620 pr_debug("%s: irq_base = %u\n", __func__, domain->irq_base);
621
622 return 0;
623}
624#endif
625
626MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>");
627MODULE_DESCRIPTION("Driver for Qualcomm MSM TLMMv2 SoC GPIOs");
628MODULE_LICENSE("GPL v2");
629MODULE_ALIAS("sysdev:msmgpio");