blob: fe7ffff51a7df7647bb070d0ebc5d3c42d2d3aac [file] [log] [blame]
Taniya Dasfcb35002012-03-09 15:28:12 +05301/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
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,
95 [MSM8625_INT_A9_M2A_5] = SMSM_FAKE_IRQ,
96 [MSM8625_INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ,
97 [MSM8625_INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ,
98 [MSM8625_INT_ADSP_A11] = SMSM_FAKE_IRQ,
99};
100
Murali Nalajalabac37912012-08-28 18:15:43 +0530101static uint16_t msm_bypassed_apps_irqs[] = {
102 MSM8625_INT_CPR_IRQ0,
103};
104
105/* Check IRQ falls into bypassed list are not */
106static bool msm_mpm_bypass_apps_irq(unsigned int irq)
107{
108 int i;
109
110 for (i = 0; i < ARRAY_SIZE(msm_bypassed_apps_irqs); i++)
111 if (irq == msm_bypassed_apps_irqs[i])
112 return true;
113
114 return false;
115}
116
Taniya Dasfcb35002012-03-09 15:28:12 +0530117static void msm_gic_mask_irq(struct irq_data *d)
118{
119 unsigned int index = GIC_IRQ_INDEX(d->irq);
120 uint32_t mask;
121 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
122
123 mask = GIC_IRQ_MASK(d->irq);
124
Murali Nalajalabac37912012-08-28 18:15:43 +0530125 /* check whether irq to be bypassed are not */
126 if (msm_mpm_bypass_apps_irq(d->irq))
127 return;
128
Taniya Dasfcb35002012-03-09 15:28:12 +0530129 if (smsm_irq == 0) {
130 msm_gic_irq_idle_disable[index] &= ~mask;
131 } else {
132 mask = GIC_IRQ_MASK(smsm_irq - 1);
133 msm_gic_irq_smsm_wake_enable[0] &= ~mask;
134 }
135}
136
137static void msm_gic_unmask_irq(struct irq_data *d)
138{
139 unsigned int index = GIC_IRQ_INDEX(d->irq);
140 uint32_t mask;
141 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
142
143 mask = GIC_IRQ_MASK(d->irq);
144
Murali Nalajalabac37912012-08-28 18:15:43 +0530145 /* check whether irq to be bypassed are not */
146 if (msm_mpm_bypass_apps_irq(d->irq))
147 return;
148
Taniya Dasfcb35002012-03-09 15:28:12 +0530149 if (smsm_irq == 0) {
150 msm_gic_irq_idle_disable[index] |= mask;
151 } else {
152 mask = GIC_IRQ_MASK(smsm_irq - 1);
153 msm_gic_irq_smsm_wake_enable[0] |= mask;
154 }
155}
156
157static int msm_gic_set_irq_wake(struct irq_data *d, unsigned int on)
158{
159 uint32_t mask;
160 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
161
162 if (smsm_irq == 0) {
163 pr_err("bad wake up irq %d\n", d->irq);
164 return -EINVAL;
165 }
166
Murali Nalajalabac37912012-08-28 18:15:43 +0530167 /* check whether irq to be bypassed are not */
168 if (msm_mpm_bypass_apps_irq(d->irq))
169 return 0;
170
Taniya Dasfcb35002012-03-09 15:28:12 +0530171 if (smsm_irq == SMSM_FAKE_IRQ)
172 return 0;
173
174 mask = GIC_IRQ_MASK(smsm_irq - 1);
175 if (on)
176 msm_gic_irq_smsm_wake_enable[1] |= mask;
177 else
178 msm_gic_irq_smsm_wake_enable[1] &= ~mask;
179
180 return 0;
181}
182
Trilok Soni1a9fdee2012-05-28 19:54:11 +0530183void __init msm_gic_irq_extn_init(void)
Taniya Dasfcb35002012-03-09 15:28:12 +0530184{
Taniya Dasfcb35002012-03-09 15:28:12 +0530185 gic_arch_extn.irq_mask = msm_gic_mask_irq;
186 gic_arch_extn.irq_unmask = msm_gic_unmask_irq;
187 gic_arch_extn.irq_disable = msm_gic_mask_irq;
188 gic_arch_extn.irq_set_wake = msm_gic_set_irq_wake;
Taniya Dasfcb35002012-03-09 15:28:12 +0530189}
190
191/* Power APIs */
192
193 /*
Taniya Dasfcb35002012-03-09 15:28:12 +0530194 * Iterate over the disable list
195 */
196
197int msm_gic_irq_idle_sleep_allowed(void)
198{
199 uint32_t i, disable = 0;
200
201 for (i = 0; i < NUM_REGS_DISABLE; i++)
202 disable |= msm_gic_irq_idle_disable[i];
203
204 return !disable;
205}
206
207 /*
208 * Prepare interrupt subsystem for entering sleep -- phase 1
209 * If modem_wake is true, return currently enabled interrupt
210 * mask in *irq_mask
211 */
212void msm_gic_irq_enter_sleep1(bool modem_wake, int from_idle, uint32_t
213 *irq_mask)
214{
215 if (modem_wake) {
216 *irq_mask = msm_gic_irq_smsm_wake_enable[!from_idle];
217 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
218 pr_info("%s irq_mask %x\n", __func__, *irq_mask);
219 }
220}
221
222 /*
223 * Prepare interrupt susbsytem for entering sleep -- phase 2
224 * Detect any pending interrupts and configure interrupt hardware.
225 * Return value:
226 * -EAGAIN: there are pending interrupt(s); interrupt configuration is not
227 * changed
228 * 0: Success
229 */
230int msm_gic_irq_enter_sleep2(bool modem_wake, int from_idle)
231{
Taniya Dasfcb35002012-03-09 15:28:12 +0530232 if (from_idle && !modem_wake)
233 return 0;
234
235 /* edge triggered interrupt may get lost if this mode is used */
236 WARN_ON_ONCE(!modem_wake && !from_idle);
237
238 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
239 pr_info("%s interrupts pending\n", __func__);
240
241 /* check the pending interrupts */
242 if (msm_gic_spi_ppi_pending()) {
243 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT)
244 pr_info("%s aborted....\n", __func__);
245 return -EAGAIN;
246 }
247
248 if (modem_wake) {
Taniya Dasbc9248a2012-04-30 19:59:11 +0530249 /* save the contents of GIC CPU interface and Distributor
250 * Disable all the Interrupts, if we enter from idle pc
251 */
Trilok Soni56e7f9e2012-06-08 15:01:44 +0530252 msm_gic_save();
Taniya Das960fec42012-03-27 11:54:05 +0530253 irq_set_irq_type(MSM8625_INT_A9_M2A_6, IRQF_TRIGGER_RISING);
Taniya Dasfcb35002012-03-09 15:28:12 +0530254 enable_irq(MSM8625_INT_A9_M2A_6);
Taniya Das960fec42012-03-27 11:54:05 +0530255 pr_debug("%s going for sleep now\n", __func__);
Taniya Dasfcb35002012-03-09 15:28:12 +0530256 }
257
258 return 0;
259}
260
261 /*
262 * Restore interrupt subsystem from sleep -- phase 1
263 * Configure the interrupt hardware.
264 */
265void msm_gic_irq_exit_sleep1(uint32_t irq_mask, uint32_t wakeup_reason,
266 uint32_t pending_irqs)
267{
268 /* Restore GIC contents, which were saved */
269 msm_gic_restore();
270
271 /* Disable A9_M2A_6 */
272 disable_irq(MSM8625_INT_A9_M2A_6);
273
274 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
275 pr_info("%s %x %x %x now\n", __func__, irq_mask,
276 pending_irqs, wakeup_reason);
277}
278
279 /*
280 * Restore interrupt subsystem from sleep -- phase 2
281 * Poke the specified pending interrupts into interrupt hardware.
282 */
283void msm_gic_irq_exit_sleep2(uint32_t irq_mask, uint32_t wakeup_reason,
284 uint32_t pending)
285{
286 int i, smsm_irq, smsm_mask;
287 struct irq_desc *desc;
288
289 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
290 pr_info("%s %x %x %x now\n", __func__, irq_mask,
291 pending, wakeup_reason);
292
293 for (i = 0; pending && i < ARRAY_SIZE(msm_gic_irq_to_smsm); i++) {
294 smsm_irq = msm_gic_irq_to_smsm[i];
295
296 if (smsm_irq == 0)
297 continue;
298
299 smsm_mask = BIT(smsm_irq - 1);
300 if (!(pending & smsm_mask))
301 continue;
302
303 pending &= ~smsm_mask;
304
305 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_INT)
306 pr_info("%s, irq %d, still pending %x now\n",
307 __func__, i, pending);
308 /* Peding IRQ */
309 desc = i ? irq_to_desc(i) : NULL;
310
311 /* Check if the pending */
312 if (desc && !irqd_is_level_type(&desc->irq_data)) {
313 /* Mark the IRQ as pending, if not Level */
314 irq_set_pending(i);
315 check_irq_resend(desc, i);
316 }
317 }
318}
319
320 /*
321 * Restore interrupt subsystem from sleep -- phase 3
322 * Print debug information
323 */
324void msm_gic_irq_exit_sleep3(uint32_t irq_mask, uint32_t wakeup_reason,
325 uint32_t pending_irqs)
326{
327 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
328 pr_info("%s, irq_mask %x pending_irqs %x, wakeup_reason %x,"
329 "state %x now\n", __func__, irq_mask,
330 pending_irqs, wakeup_reason,
331 smsm_get_state(SMSM_MODEM_STATE));
332}