Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux...
[pandora-kernel.git] / arch / arm / plat-spear / shirq.c
1 /*
2  * arch/arm/plat-spear/shirq.c
3  *
4  * SPEAr platform shared irq layer source file
5  *
6  * Copyright (C) 2009 ST Microelectronics
7  * Viresh Kumar<viresh.kumar@st.com>
8  *
9  * This file is licensed under the terms of the GNU General Public
10  * License version 2. This program is licensed "as is" without any
11  * warranty of any kind, whether express or implied.
12  */
13
14 #include <linux/err.h>
15 #include <linux/io.h>
16 #include <linux/irq.h>
17 #include <linux/spinlock.h>
18 #include <plat/shirq.h>
19
20 struct spear_shirq *shirq;
21 static DEFINE_SPINLOCK(lock);
22
23 static void shirq_irq_mask(unsigned irq)
24 {
25         struct spear_shirq *shirq = get_irq_chip_data(irq);
26         u32 val, id = irq - shirq->dev_config[0].virq;
27         unsigned long flags;
28
29         if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
30                 return;
31
32         spin_lock_irqsave(&lock, flags);
33         val = readl(shirq->regs.base + shirq->regs.enb_reg);
34         if (shirq->regs.reset_to_enb)
35                 val |= shirq->dev_config[id].enb_mask;
36         else
37                 val &= ~(shirq->dev_config[id].enb_mask);
38         writel(val, shirq->regs.base + shirq->regs.enb_reg);
39         spin_unlock_irqrestore(&lock, flags);
40 }
41
42 static void shirq_irq_unmask(unsigned irq)
43 {
44         struct spear_shirq *shirq = get_irq_chip_data(irq);
45         u32 val, id = irq - shirq->dev_config[0].virq;
46         unsigned long flags;
47
48         if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
49                 return;
50
51         spin_lock_irqsave(&lock, flags);
52         val = readl(shirq->regs.base + shirq->regs.enb_reg);
53         if (shirq->regs.reset_to_enb)
54                 val &= ~(shirq->dev_config[id].enb_mask);
55         else
56                 val |= shirq->dev_config[id].enb_mask;
57         writel(val, shirq->regs.base + shirq->regs.enb_reg);
58         spin_unlock_irqrestore(&lock, flags);
59 }
60
61 static struct irq_chip shirq_chip = {
62         .name           = "spear_shirq",
63         .ack            = shirq_irq_mask,
64         .mask           = shirq_irq_mask,
65         .unmask         = shirq_irq_unmask,
66 };
67
68 static void shirq_handler(unsigned irq, struct irq_desc *desc)
69 {
70         u32 i, val, mask;
71         struct spear_shirq *shirq = get_irq_data(irq);
72
73         desc->chip->ack(irq);
74         while ((val = readl(shirq->regs.base + shirq->regs.status_reg) &
75                                 shirq->regs.status_reg_mask)) {
76                 for (i = 0; (i < shirq->dev_count) && val; i++) {
77                         if (!(shirq->dev_config[i].status_mask & val))
78                                 continue;
79
80                         generic_handle_irq(shirq->dev_config[i].virq);
81
82                         /* clear interrupt */
83                         val &= ~shirq->dev_config[i].status_mask;
84                         if ((shirq->regs.clear_reg == -1) ||
85                                         shirq->dev_config[i].clear_mask == -1)
86                                 continue;
87                         mask = readl(shirq->regs.base + shirq->regs.clear_reg);
88                         if (shirq->regs.reset_to_clear)
89                                 mask &= ~shirq->dev_config[i].clear_mask;
90                         else
91                                 mask |= shirq->dev_config[i].clear_mask;
92                         writel(mask, shirq->regs.base + shirq->regs.clear_reg);
93                 }
94         }
95         desc->chip->unmask(irq);
96 }
97
98 int spear_shirq_register(struct spear_shirq *shirq)
99 {
100         int i;
101
102         if (!shirq || !shirq->dev_config || !shirq->regs.base)
103                 return -EFAULT;
104
105         if (!shirq->dev_count)
106                 return -EINVAL;
107
108         set_irq_chained_handler(shirq->irq, shirq_handler);
109         for (i = 0; i < shirq->dev_count; i++) {
110                 set_irq_chip(shirq->dev_config[i].virq, &shirq_chip);
111                 set_irq_handler(shirq->dev_config[i].virq, handle_simple_irq);
112                 set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID);
113                 set_irq_chip_data(shirq->dev_config[i].virq, shirq);
114         }
115
116         set_irq_data(shirq->irq, shirq);
117         return 0;
118 }