Merge branch 'for-2.6.37' into HEAD
[pandora-kernel.git] / arch / arm / mach-ux500 / modem_irq.c
1 /*
2  * Copyright (C) ST-Ericsson SA 2010
3  * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson.
4  * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson.
5  * License terms: GNU General Public License (GPL), version 2.
6  */
7
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/irq.h>
11 #include <linux/interrupt.h>
12 #include <linux/io.h>
13 #include <linux/slab.h>
14
15 #define MODEM_INTCON_BASE_ADDR 0xBFFD3000
16 #define MODEM_INTCON_SIZE 0xFFF
17
18 #define DEST_IRQ41_OFFSET 0x2A4
19 #define DEST_IRQ43_OFFSET 0x2AC
20 #define DEST_IRQ45_OFFSET 0x2B4
21
22 #define PRIO_IRQ41_OFFSET 0x6A4
23 #define PRIO_IRQ43_OFFSET 0x6AC
24 #define PRIO_IRQ45_OFFSET 0x6B4
25
26 #define ALLOW_IRQ_OFFSET 0x104
27
28 #define MODEM_INTCON_CPU_NBR 0x1
29 #define MODEM_INTCON_PRIO_HIGH 0x0
30
31 #define MODEM_INTCON_ALLOW_IRQ41 0x0200
32 #define MODEM_INTCON_ALLOW_IRQ43 0x0800
33 #define MODEM_INTCON_ALLOW_IRQ45 0x2000
34
35 #define MODEM_IRQ_REG_OFFSET 0x4
36
37 struct modem_irq {
38         void __iomem *modem_intcon_base;
39 };
40
41
42 static void setup_modem_intcon(void __iomem *modem_intcon_base)
43 {
44         /* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */
45         writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET);
46         writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET);
47         writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET);
48
49         /* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */
50         writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET);
51         writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET);
52         writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET);
53
54         /* IC_ALLOW_ARRAY - IRQ enable */
55         writel(MODEM_INTCON_ALLOW_IRQ41 |
56                    MODEM_INTCON_ALLOW_IRQ43 |
57                    MODEM_INTCON_ALLOW_IRQ45,
58                    modem_intcon_base + ALLOW_IRQ_OFFSET);
59 }
60
61 static irqreturn_t modem_cpu_irq_handler(int irq, void *data)
62 {
63         int real_irq;
64         int virt_irq;
65         struct modem_irq *mi = (struct modem_irq *)data;
66
67         /* Read modem side IRQ number from modem IRQ controller */
68         real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF;
69         virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq;
70
71         pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X "
72                  "which will be 0x%X (%d) which translates to "
73                  "virtual IRQ 0x%X (%d)!\n",
74                    (u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET,
75                    real_irq,
76                    real_irq & 0xFF,
77                    real_irq & 0xFF,
78                    virt_irq,
79                    virt_irq);
80
81         if (virt_irq != 0)
82                 generic_handle_irq(virt_irq);
83
84         pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq);
85
86         return IRQ_HANDLED;
87 }
88
89 static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip)
90 {
91         set_irq_chip(irq, modem_irq_chip);
92         set_irq_handler(irq, handle_simple_irq);
93         set_irq_flags(irq, IRQF_VALID);
94
95         pr_debug("modem_irq: Created virtual IRQ %d\n", irq);
96 }
97
98 static int modem_irq_init(void)
99 {
100         int err;
101         static struct irq_chip  modem_irq_chip;
102         struct modem_irq *mi;
103
104         pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n",
105                    IRQ_DB5500_MODEM);
106
107         mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL);
108         if (!mi) {
109                 pr_err("modem_irq: Could not allocate device\n");
110                 return -ENOMEM;
111         }
112
113         mi->modem_intcon_base =
114                 ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE);
115         pr_debug("modem_irq: ioremapped modem_intcon_base from "
116                  "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR,
117                  (u32)mi->modem_intcon_base);
118
119         setup_modem_intcon(mi->modem_intcon_base);
120
121         modem_irq_chip = dummy_irq_chip;
122         modem_irq_chip.name = "modem_irq";
123
124         /* Create the virtual IRQ:s needed */
125         create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip);
126         create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip);
127         create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip);
128
129         err = request_threaded_irq(IRQ_DB5500_MODEM, NULL,
130                                    modem_cpu_irq_handler, IRQF_ONESHOT,
131                                    "modem_irq", mi);
132         if (err)
133                 pr_err("modem_irq: Could not register IRQ %d\n",
134                        IRQ_DB5500_MODEM);
135
136         return 0;
137 }
138
139 arch_initcall(modem_irq_init);