blob: 954e5ccd2069c7b0809e329588d77628ab632e08 [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
101static void msm_gic_mask_irq(struct irq_data *d)
102{
103 unsigned int index = GIC_IRQ_INDEX(d->irq);
104 uint32_t mask;
105 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
106
107 mask = GIC_IRQ_MASK(d->irq);
108
109 if (smsm_irq == 0) {
110 msm_gic_irq_idle_disable[index] &= ~mask;
111 } else {
112 mask = GIC_IRQ_MASK(smsm_irq - 1);
113 msm_gic_irq_smsm_wake_enable[0] &= ~mask;
114 }
115}
116
117static void msm_gic_unmask_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
125 if (smsm_irq == 0) {
126 msm_gic_irq_idle_disable[index] |= mask;
127 } else {
128 mask = GIC_IRQ_MASK(smsm_irq - 1);
129 msm_gic_irq_smsm_wake_enable[0] |= mask;
130 }
131}
132
133static int msm_gic_set_irq_wake(struct irq_data *d, unsigned int on)
134{
135 uint32_t mask;
136 int smsm_irq = msm_gic_irq_to_smsm[d->irq];
137
138 if (smsm_irq == 0) {
139 pr_err("bad wake up irq %d\n", d->irq);
140 return -EINVAL;
141 }
142
143 if (smsm_irq == SMSM_FAKE_IRQ)
144 return 0;
145
146 mask = GIC_IRQ_MASK(smsm_irq - 1);
147 if (on)
148 msm_gic_irq_smsm_wake_enable[1] |= mask;
149 else
150 msm_gic_irq_smsm_wake_enable[1] &= ~mask;
151
152 return 0;
153}
154
Trilok Soni1a9fdee2012-05-28 19:54:11 +0530155void __init msm_gic_irq_extn_init(void)
Taniya Dasfcb35002012-03-09 15:28:12 +0530156{
Taniya Dasfcb35002012-03-09 15:28:12 +0530157 gic_arch_extn.irq_mask = msm_gic_mask_irq;
158 gic_arch_extn.irq_unmask = msm_gic_unmask_irq;
159 gic_arch_extn.irq_disable = msm_gic_mask_irq;
160 gic_arch_extn.irq_set_wake = msm_gic_set_irq_wake;
Taniya Dasfcb35002012-03-09 15:28:12 +0530161}
162
163/* Power APIs */
164
165 /*
Taniya Dasfcb35002012-03-09 15:28:12 +0530166 * Iterate over the disable list
167 */
168
169int msm_gic_irq_idle_sleep_allowed(void)
170{
171 uint32_t i, disable = 0;
172
173 for (i = 0; i < NUM_REGS_DISABLE; i++)
174 disable |= msm_gic_irq_idle_disable[i];
175
176 return !disable;
177}
178
179 /*
180 * Prepare interrupt subsystem for entering sleep -- phase 1
181 * If modem_wake is true, return currently enabled interrupt
182 * mask in *irq_mask
183 */
184void msm_gic_irq_enter_sleep1(bool modem_wake, int from_idle, uint32_t
185 *irq_mask)
186{
187 if (modem_wake) {
188 *irq_mask = msm_gic_irq_smsm_wake_enable[!from_idle];
189 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
190 pr_info("%s irq_mask %x\n", __func__, *irq_mask);
191 }
192}
193
194 /*
195 * Prepare interrupt susbsytem for entering sleep -- phase 2
196 * Detect any pending interrupts and configure interrupt hardware.
197 * Return value:
198 * -EAGAIN: there are pending interrupt(s); interrupt configuration is not
199 * changed
200 * 0: Success
201 */
202int msm_gic_irq_enter_sleep2(bool modem_wake, int from_idle)
203{
Taniya Dasfcb35002012-03-09 15:28:12 +0530204 if (from_idle && !modem_wake)
205 return 0;
206
207 /* edge triggered interrupt may get lost if this mode is used */
208 WARN_ON_ONCE(!modem_wake && !from_idle);
209
210 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
211 pr_info("%s interrupts pending\n", __func__);
212
213 /* check the pending interrupts */
214 if (msm_gic_spi_ppi_pending()) {
215 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT)
216 pr_info("%s aborted....\n", __func__);
217 return -EAGAIN;
218 }
219
220 if (modem_wake) {
Taniya Dasbc9248a2012-04-30 19:59:11 +0530221 /* save the contents of GIC CPU interface and Distributor
222 * Disable all the Interrupts, if we enter from idle pc
223 */
224 msm_gic_save(modem_wake, from_idle);
Taniya Das960fec42012-03-27 11:54:05 +0530225 irq_set_irq_type(MSM8625_INT_A9_M2A_6, IRQF_TRIGGER_RISING);
Taniya Dasfcb35002012-03-09 15:28:12 +0530226 enable_irq(MSM8625_INT_A9_M2A_6);
Taniya Das960fec42012-03-27 11:54:05 +0530227 pr_debug("%s going for sleep now\n", __func__);
Taniya Dasfcb35002012-03-09 15:28:12 +0530228 }
229
230 return 0;
231}
232
233 /*
234 * Restore interrupt subsystem from sleep -- phase 1
235 * Configure the interrupt hardware.
236 */
237void msm_gic_irq_exit_sleep1(uint32_t irq_mask, uint32_t wakeup_reason,
238 uint32_t pending_irqs)
239{
240 /* Restore GIC contents, which were saved */
241 msm_gic_restore();
242
243 /* Disable A9_M2A_6 */
244 disable_irq(MSM8625_INT_A9_M2A_6);
245
246 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
247 pr_info("%s %x %x %x now\n", __func__, irq_mask,
248 pending_irqs, wakeup_reason);
249}
250
251 /*
252 * Restore interrupt subsystem from sleep -- phase 2
253 * Poke the specified pending interrupts into interrupt hardware.
254 */
255void msm_gic_irq_exit_sleep2(uint32_t irq_mask, uint32_t wakeup_reason,
256 uint32_t pending)
257{
258 int i, smsm_irq, smsm_mask;
259 struct irq_desc *desc;
260
261 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
262 pr_info("%s %x %x %x now\n", __func__, irq_mask,
263 pending, wakeup_reason);
264
265 for (i = 0; pending && i < ARRAY_SIZE(msm_gic_irq_to_smsm); i++) {
266 smsm_irq = msm_gic_irq_to_smsm[i];
267
268 if (smsm_irq == 0)
269 continue;
270
271 smsm_mask = BIT(smsm_irq - 1);
272 if (!(pending & smsm_mask))
273 continue;
274
275 pending &= ~smsm_mask;
276
277 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP_INT)
278 pr_info("%s, irq %d, still pending %x now\n",
279 __func__, i, pending);
280 /* Peding IRQ */
281 desc = i ? irq_to_desc(i) : NULL;
282
283 /* Check if the pending */
284 if (desc && !irqd_is_level_type(&desc->irq_data)) {
285 /* Mark the IRQ as pending, if not Level */
286 irq_set_pending(i);
287 check_irq_resend(desc, i);
288 }
289 }
290}
291
292 /*
293 * Restore interrupt subsystem from sleep -- phase 3
294 * Print debug information
295 */
296void msm_gic_irq_exit_sleep3(uint32_t irq_mask, uint32_t wakeup_reason,
297 uint32_t pending_irqs)
298{
299 if (msm_gic_irq_debug_mask & IRQ_DEBUG_SLEEP)
300 pr_info("%s, irq_mask %x pending_irqs %x, wakeup_reason %x,"
301 "state %x now\n", __func__, irq_mask,
302 pending_irqs, wakeup_reason,
303 smsm_get_state(SMSM_MODEM_STATE));
304}