blob: e1296a7447c8493eabccf5ce189b4a0aaf1a6ed6 [file] [log] [blame]
Linus Walleijb84ccd72010-09-27 22:10:55 +01001/*
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
Rabin Vincent5ec65522010-12-08 11:08:00 +053015#include <mach/id.h>
16
Linus Walleijb84ccd72010-09-27 22:10:55 +010017#define MODEM_INTCON_BASE_ADDR 0xBFFD3000
18#define MODEM_INTCON_SIZE 0xFFF
19
20#define DEST_IRQ41_OFFSET 0x2A4
21#define DEST_IRQ43_OFFSET 0x2AC
22#define DEST_IRQ45_OFFSET 0x2B4
23
24#define PRIO_IRQ41_OFFSET 0x6A4
25#define PRIO_IRQ43_OFFSET 0x6AC
26#define PRIO_IRQ45_OFFSET 0x6B4
27
28#define ALLOW_IRQ_OFFSET 0x104
29
30#define MODEM_INTCON_CPU_NBR 0x1
31#define MODEM_INTCON_PRIO_HIGH 0x0
32
33#define MODEM_INTCON_ALLOW_IRQ41 0x0200
34#define MODEM_INTCON_ALLOW_IRQ43 0x0800
35#define MODEM_INTCON_ALLOW_IRQ45 0x2000
36
37#define MODEM_IRQ_REG_OFFSET 0x4
38
39struct modem_irq {
40 void __iomem *modem_intcon_base;
41};
42
43
44static void setup_modem_intcon(void __iomem *modem_intcon_base)
45{
46 /* IC_DESTINATION_BASE_ARRAY - Which CPU to receive the IRQ */
47 writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ41_OFFSET);
48 writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ43_OFFSET);
49 writel(MODEM_INTCON_CPU_NBR, modem_intcon_base + DEST_IRQ45_OFFSET);
50
51 /* IC_PRIORITY_BASE_ARRAY - IRQ priority in modem IRQ controller */
52 writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ41_OFFSET);
53 writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ43_OFFSET);
54 writel(MODEM_INTCON_PRIO_HIGH, modem_intcon_base + PRIO_IRQ45_OFFSET);
55
56 /* IC_ALLOW_ARRAY - IRQ enable */
57 writel(MODEM_INTCON_ALLOW_IRQ41 |
58 MODEM_INTCON_ALLOW_IRQ43 |
59 MODEM_INTCON_ALLOW_IRQ45,
60 modem_intcon_base + ALLOW_IRQ_OFFSET);
61}
62
63static irqreturn_t modem_cpu_irq_handler(int irq, void *data)
64{
65 int real_irq;
66 int virt_irq;
67 struct modem_irq *mi = (struct modem_irq *)data;
68
69 /* Read modem side IRQ number from modem IRQ controller */
70 real_irq = readl(mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET) & 0xFF;
71 virt_irq = IRQ_MODEM_EVENTS_BASE + real_irq;
72
73 pr_debug("modem_irq: Worker read addr 0x%X and got value 0x%X "
74 "which will be 0x%X (%d) which translates to "
75 "virtual IRQ 0x%X (%d)!\n",
76 (u32)mi->modem_intcon_base + MODEM_IRQ_REG_OFFSET,
77 real_irq,
78 real_irq & 0xFF,
79 real_irq & 0xFF,
80 virt_irq,
81 virt_irq);
82
83 if (virt_irq != 0)
84 generic_handle_irq(virt_irq);
85
86 pr_debug("modem_irq: Done handling virtual IRQ %d!\n", virt_irq);
87
88 return IRQ_HANDLED;
89}
90
91static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip)
92{
93 set_irq_chip(irq, modem_irq_chip);
94 set_irq_handler(irq, handle_simple_irq);
95 set_irq_flags(irq, IRQF_VALID);
96
97 pr_debug("modem_irq: Created virtual IRQ %d\n", irq);
98}
99
100static int modem_irq_init(void)
101{
102 int err;
103 static struct irq_chip modem_irq_chip;
104 struct modem_irq *mi;
105
Rabin Vincent5ec65522010-12-08 11:08:00 +0530106 if (!cpu_is_u5500())
107 return -ENODEV;
108
Linus Walleijb84ccd72010-09-27 22:10:55 +0100109 pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n",
110 IRQ_DB5500_MODEM);
111
112 mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL);
113 if (!mi) {
114 pr_err("modem_irq: Could not allocate device\n");
115 return -ENOMEM;
116 }
117
118 mi->modem_intcon_base =
119 ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE);
120 pr_debug("modem_irq: ioremapped modem_intcon_base from "
121 "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR,
122 (u32)mi->modem_intcon_base);
123
124 setup_modem_intcon(mi->modem_intcon_base);
125
126 modem_irq_chip = dummy_irq_chip;
127 modem_irq_chip.name = "modem_irq";
128
129 /* Create the virtual IRQ:s needed */
130 create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip);
131 create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip);
132 create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip);
133
134 err = request_threaded_irq(IRQ_DB5500_MODEM, NULL,
135 modem_cpu_irq_handler, IRQF_ONESHOT,
136 "modem_irq", mi);
137 if (err)
138 pr_err("modem_irq: Could not register IRQ %d\n",
139 IRQ_DB5500_MODEM);
140
141 return 0;
142}
143
144arch_initcall(modem_irq_init);