blob: 6c2ed76b539fe9e50da41bd1ff01d2b7c730bd69 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Taniya Dasfcb35002012-03-09 15:28:12 +05302 *
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
14#include <linux/bitops.h>
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/delay.h>
18#include <linux/types.h>
19#include <linux/init.h>
20#include <linux/interrupt.h>
21#include <linux/io.h>
22#include <linux/irq.h>
23#include <linux/spinlock.h>
24
25#include <asm/hardware/gic.h>
26#include <mach/msm_smsm.h>
27
Taniya Dasbc9248a2012-04-30 19:59:11 +053028#include "mpm-8625.h"
29
Taniya Dasfcb35002012-03-09 15:28:12 +053030#define NUM_REGS_ENABLE 2
31/* (NR_MSM_IRQS/32) 96 max irqs supported */
32#define NUM_REGS_DISABLE 3
33#define GIC_IRQ_MASK(irq) BIT(irq % 32)
34#define GIC_IRQ_INDEX(irq) (irq / 32)
35
Taniya Dasfcb35002012-03-09 15:28:12 +053036enum {
37 IRQ_DEBUG_SLEEP_INT_TRIGGER = BIT(0),
38 IRQ_DEBUG_SLEEP_INT = BIT(1),
39 IRQ_DEBUG_SLEEP_ABORT = BIT(2),
40 IRQ_DEBUG_SLEEP = BIT(3),
41 IRQ_DEBUG_SLEEP_REQUEST = BIT(4)
42};
43
44static int msm_gic_irq_debug_mask;
45module_param_named(debug_mask, msm_gic_irq_debug_mask, int,
46 S_IRUGO | S_IWUSR | S_IWGRP);
47
48static uint32_t msm_gic_irq_smsm_wake_enable[NUM_REGS_ENABLE];
49static uint32_t msm_gic_irq_idle_disable[NUM_REGS_DISABLE];
50
Taniya Dasfcb35002012-03-09 15:28:12 +053051 /*
52 * Some of the interrupts which will not be considered as wake capable
53 * should be marked as FAKE.
54 * Interrupts: GPIO, Timers etc..
55 */
56#define SMSM_FAKE_IRQ (0xff)
57
58 /* msm_gic_irq_to_smsm: IRQ's those will be monitored by Modem */
59static uint8_t msm_gic_irq_to_smsm[NR_IRQS] = {
60 [MSM8625_INT_USB_OTG] = 4,
61 [MSM8625_INT_PWB_I2C] = 5,
62 [MSM8625_INT_SDC1_0] = 6,
63 [MSM8625_INT_SDC1_1] = 7,
64 [MSM8625_INT_SDC2_0] = 8,
65 [MSM8625_INT_SDC2_1] = 9,
66 [MSM8625_INT_ADSP_A9_A11] = 10,
67 [MSM8625_INT_UART1] = 11,
68 [MSM8625_INT_UART2] = 12,
69 [MSM8625_INT_UART3] = 13,
70 [MSM8625_INT_UART1_RX] = 14,
71 [MSM8625_INT_UART2_RX] = 15,
72 [MSM8625_INT_UART3_RX] = 16,
73 [MSM8625_INT_UART1DM_IRQ] = 17,
74 [MSM8625_INT_UART1DM_RX] = 18,
75 [MSM8625_INT_KEYSENSE] = 19,
76 [MSM8625_INT_AD_HSSD] = 20,
77 [MSM8625_INT_NAND_WR_ER_DONE] = 21,
78 [MSM8625_INT_NAND_OP_DONE] = 22,
79 [MSM8625_INT_TCHSCRN1] = 23,
80 [MSM8625_INT_TCHSCRN2] = 24,
81 [MSM8625_INT_TCHSCRN_SSBI] = 25,
82 [MSM8625_INT_USB_HS] = 26,
83 [MSM8625_INT_UART2DM_RX] = 27,
84 [MSM8625_INT_UART2DM_IRQ] = 28,
85 [MSM8625_INT_SDC4_1] = 29,
86 [MSM8625_INT_SDC4_0] = 30,
87 [MSM8625_INT_SDC3_1] = 31,
88 [MSM8625_INT_SDC3_0] = 32,
89
90 /* fake wakeup interrupts */
91 [MSM8625_INT_GPIO_GROUP1] = SMSM_FAKE_IRQ,
92 [MSM8625_INT_GPIO_GROUP2] = SMSM_FAKE_IRQ,
93 [MSM8625_INT_A9_M2A_0] = SMSM_FAKE_IRQ,
94 [MSM8625_INT_A9_M2A_1] = SMSM_FAKE_IRQ,
Prasad Sodagudied8df5b2012-09-28 13:49:59 +053095 [MSM8625_INT_A9_M2A_2] = SMSM_FAKE_IRQ,
Taniya Dasfcb35002012-03-09 15:28:12 +053096 [MSM8625_INT_A9_M2A_5] = SMSM_FAKE_IRQ,
97 [MSM8625_INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ,
98 [MSM8625_INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ,
99 [MSM8625_INT_ADSP_A11] = SMSM_FAKE_IRQ,
100};
101
Murali Nalajala613904c2012-08-28 18:15:43 +0530102static uint16_t msm_bypassed_apps_irqs[] = {
103 MSM8625_INT_CPR_IRQ0,
Ashwin Chaugule3deb6b82012-12-21 13:09:48 -0500104 MSM8625_INT_SC_SICL2PERFMONIRPTREQ,
Murali Nalajala613904c2012-08-28 18:15:43 +0530105};
106
107/* Check IRQ falls into bypassed list are not */
108static bool msm_mpm_bypass_apps_irq(unsigned int irq)
109{
110 int i;
111
112 for (i = 0; i < ARRAY_SIZE(msm_bypassed_apps_irqs); i++)
113 if (irq == msm_bypassed_apps_irqs[i])
114 return true;
115
116 return false;
117}
118
Taniya Dasfcb35002012-03-09 15:28:12 +0530119static void msm_gic_mask_irq(struct irq_data *d)
120{
121 unsigned int index = GIC_IRQ_INDEX(d->irq);
122 uint32_t mask;
123 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
124
125 mask = GIC_IRQ_MASK(d->irq);
126
Murali Nalajala613904c2012-08-28 18:15:43 +0530127 /* check whether irq to be bypassed are not */
128 if (msm_mpm_bypass_apps_irq(d->irq))
129 return;
130
Taniya Dasfcb35002012-03-09 15:28:12 +0530131 if (smsm_irq == 0) {
132 msm_gic_irq_idle_disable[index] &= ~mask;
133 } else {
134 mask = GIC_IRQ_MASK(smsm_irq - 1);
135 msm_gic_irq_smsm_wake_enable[0] &= ~mask;
136 }
137}
138
139static void msm_gic_unmask_irq(struct irq_data *d)
140{
141 unsigned int index = GIC_IRQ_INDEX(d->irq);
142 uint32_t mask;
143 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
144
145 mask = GIC_IRQ_MASK(d->irq);
146
Murali Nalajala613904c2012-08-28 18:15:43 +0530147 /* check whether irq to be bypassed are not */
148 if (msm_mpm_bypass_apps_irq(d->irq))
149 return;
150
Taniya Dasfcb35002012-03-09 15:28:12 +0530151 if (smsm_irq == 0) {
152 msm_gic_irq_idle_disable[index] |= mask;
153 } else {
154 mask = GIC_IRQ_MASK(smsm_irq - 1);
155 msm_gic_irq_smsm_wake_enable[0] |= mask;
156 }
157}
158
159static int msm_gic_set_irq_wake(struct irq_data *d, unsigned int on)
160{
161 uint32_t mask;
162 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
163
164 if (smsm_irq == 0) {
165 pr_err("bad wake up irq %d\n", d->irq);
166 return -EINVAL;
167 }
168
Murali Nalajala613904c2012-08-28 18:15:43 +0530169 /* check whether irq to be bypassed are not */
170 if (msm_mpm_bypass_apps_irq(d->irq))
171 return 0;
172
Taniya Dasfcb35002012-03-09 15:28:12 +0530173 if (smsm_irq == SMSM_FAKE_IRQ)
174 return 0;
175
176 mask = GIC_IRQ_MASK(smsm_irq - 1);
177 if (on)
178 msm_gic_irq_smsm_wake_enable[1] |= mask;
179 else
180 msm_gic_irq_smsm_wake_enable[1] &= ~mask;
181
182 return 0;
183}
184
Trilok Soni1a9fdee2012-05-28 19:54:11 +0530185void __init msm_gic_irq_extn_init(void)
Taniya Dasfcb35002012-03-09 15:28:12 +0530186{
Taniya Dasfcb35002012-03-09 15:28:12 +0530187 gic_arch_extn.irq_mask = msm_gic_mask_irq;
188 gic_arch_extn.irq_unmask = msm_gic_unmask_irq;
189 gic_arch_extn.irq_disable = msm_gic_mask_irq;
190 gic_arch_extn.irq_set_wake = msm_gic_set_irq_wake;
Taniya Dasfcb35002012-03-09 15:28:12 +0530191}
192
193/* Power APIs */
194
195 /*
Taniya Dasfcb35002012-03-09 15:28:12 +0530196 * Iterate over the disable list
197 */
198
199int msm_gic_irq_idle_sleep_allowed(void)
200{
201 uint32_t i, disable = 0;
202
203 for (i = 0; i < NUM_REGS_DISABLE; i++)
204 disable |= msm_gic_irq_idle_disable[i];
205
206 return !disable;
207}
208
209 /*
210 * Prepare interrupt subsystem for entering sleep -- phase 1
211 * If modem_wake is true, return currently enabled interrupt
212 * mask in *irq_mask
213 */
214void msm_gic_irq_enter_sleep1(bool modem_wake, int from_idle, uint32_t
215 *irq_mask)
216{
217 if (modem_wake) {
218 *irq_mask = msm_gic_irq_smsm_wake_enable[!from_idle];
219 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
220 pr_info("%s irq_mask %x\n", __func__, *irq_mask);
221 }
222}
223
224 /*
225 * Prepare interrupt susbsytem for entering sleep -- phase 2
226 * Detect any pending interrupts and configure interrupt hardware.
227 * Return value:
228 * -EAGAIN: there are pending interrupt(s); interrupt configuration is not
229 * changed
230 * 0: Success
231 */
232int msm_gic_irq_enter_sleep2(bool modem_wake, int from_idle)
233{
Taniya Dasfcb35002012-03-09 15:28:12 +0530234 if (from_idle && !modem_wake)
235 return 0;
236
237 /* edge triggered interrupt may get lost if this mode is used */
238 WARN_ON_ONCE(!modem_wake && !from_idle);
239
240 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
241 pr_info("%s interrupts pending\n", __func__);
242
243 /* check the pending interrupts */
244 if (msm_gic_spi_ppi_pending()) {
245 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT)
246 pr_info("%s aborted....\n", __func__);
247 return -EAGAIN;
248 }
249
250 if (modem_wake) {
Taniya Dasbc9248a2012-04-30 19:59:11 +0530251 /* save the contents of GIC CPU interface and Distributor
252 * Disable all the Interrupts, if we enter from idle pc
253 */
Trilok Soni56e7f9e2012-06-08 15:01:44 +0530254 msm_gic_save();
Taniya Das960fec42012-03-27 11:54:05 +0530255 irq_set_irq_type(MSM8625_INT_A9_M2A_6, IRQF_TRIGGER_RISING);
Taniya Dasfcb35002012-03-09 15:28:12 +0530256 enable_irq(MSM8625_INT_A9_M2A_6);
Taniya Das960fec42012-03-27 11:54:05 +0530257 pr_debug("%s going for sleep now\n", __func__);
Taniya Dasfcb35002012-03-09 15:28:12 +0530258 }
259
260 return 0;
261}
262
263 /*
264 * Restore interrupt subsystem from sleep -- phase 1
265 * Configure the interrupt hardware.
266 */
267void msm_gic_irq_exit_sleep1(uint32_t irq_mask, uint32_t wakeup_reason,
268 uint32_t pending_irqs)
269{
270 /* Restore GIC contents, which were saved */
271 msm_gic_restore();
272
273 /* Disable A9_M2A_6 */
274 disable_irq(MSM8625_INT_A9_M2A_6);
275
276 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
277 pr_info("%s %x %x %x now\n", __func__, irq_mask,
278 pending_irqs, wakeup_reason);
279}
280
281 /*
282 * Restore interrupt subsystem from sleep -- phase 2
283 * Poke the specified pending interrupts into interrupt hardware.
284 */
285void msm_gic_irq_exit_sleep2(uint32_t irq_mask, uint32_t wakeup_reason,
286 uint32_t pending)
287{
288 int i, smsm_irq, smsm_mask;
289 struct irq_desc *desc;
290
291 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
292 pr_info("%s %x %x %x now\n", __func__, irq_mask,
293 pending, wakeup_reason);
294
295 for (i = 0; pending && i < ARRAY_SIZE(msm_gic_irq_to_smsm); i++) {
296 smsm_irq = msm_gic_irq_to_smsm[i];
297
298 if (smsm_irq == 0)
299 continue;
300
301 smsm_mask = BIT(smsm_irq - 1);
302 if (!(pending & smsm_mask))
303 continue;
304
305 pending &= ~smsm_mask;
306
307 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_INT)
308 pr_info("%s, irq %d, still pending %x now\n",
309 __func__, i, pending);
310 /* Peding IRQ */
311 desc = i ? irq_to_desc(i) : NULL;
312
313 /* Check if the pending */
314 if (desc && !irqd_is_level_type(&desc->irq_data)) {
315 /* Mark the IRQ as pending, if not Level */
316 irq_set_pending(i);
317 check_irq_resend(desc, i);
318 }
319 }
320}
321
322 /*
323 * Restore interrupt subsystem from sleep -- phase 3
324 * Print debug information
325 */
326void msm_gic_irq_exit_sleep3(uint32_t irq_mask, uint32_t wakeup_reason,
327 uint32_t pending_irqs)
328{
329 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
330 pr_info("%s, irq_mask %x pending_irqs %x, wakeup_reason %x,"
331 "state %x now\n", __func__, irq_mask,
332 pending_irqs, wakeup_reason,
333 smsm_get_state(SMSM_MODEM_STATE));
334}