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