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 #include <mach/id.h>
16 
17 #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 
39 struct modem_irq {
40 	void __iomem *modem_intcon_base;
41 };
42 
43 
setup_modem_intcon(void __iomem * modem_intcon_base)44 static 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 
modem_cpu_irq_handler(int irq,void * data)63 static 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 
create_virtual_irq(int irq,struct irq_chip * modem_irq_chip)91 static void create_virtual_irq(int irq, struct irq_chip *modem_irq_chip)
92 {
93 	irq_set_chip_and_handler(irq, modem_irq_chip, handle_simple_irq);
94 	set_irq_flags(irq, IRQF_VALID);
95 
96 	pr_debug("modem_irq: Created virtual IRQ %d\n", irq);
97 }
98 
modem_irq_init(void)99 static int modem_irq_init(void)
100 {
101 	int err;
102 	static struct irq_chip  modem_irq_chip;
103 	struct modem_irq *mi;
104 
105 	if (!cpu_is_u5500())
106 		return -ENODEV;
107 
108 	pr_info("modem_irq: Set up IRQ handler for incoming modem IRQ %d\n",
109 		   IRQ_DB5500_MODEM);
110 
111 	mi = kmalloc(sizeof(struct modem_irq), GFP_KERNEL);
112 	if (!mi) {
113 		pr_err("modem_irq: Could not allocate device\n");
114 		return -ENOMEM;
115 	}
116 
117 	mi->modem_intcon_base =
118 		ioremap(MODEM_INTCON_BASE_ADDR, MODEM_INTCON_SIZE);
119 	pr_debug("modem_irq: ioremapped modem_intcon_base from "
120 		 "phy 0x%x to virt 0x%x\n", MODEM_INTCON_BASE_ADDR,
121 		 (u32)mi->modem_intcon_base);
122 
123 	setup_modem_intcon(mi->modem_intcon_base);
124 
125 	modem_irq_chip = dummy_irq_chip;
126 	modem_irq_chip.name = "modem_irq";
127 
128 	/* Create the virtual IRQ:s needed */
129 	create_virtual_irq(MBOX_PAIR0_VIRT_IRQ, &modem_irq_chip);
130 	create_virtual_irq(MBOX_PAIR1_VIRT_IRQ, &modem_irq_chip);
131 	create_virtual_irq(MBOX_PAIR2_VIRT_IRQ, &modem_irq_chip);
132 
133 	err = request_threaded_irq(IRQ_DB5500_MODEM, NULL,
134 				   modem_cpu_irq_handler, IRQF_ONESHOT,
135 				   "modem_irq", mi);
136 	if (err)
137 		pr_err("modem_irq: Could not register IRQ %d\n",
138 		       IRQ_DB5500_MODEM);
139 
140 	return 0;
141 }
142 
143 arch_initcall(modem_irq_init);
144