blob: 6008619a6bee4d653cd8586c6034d3baf95b748f [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* linux/arch/arm/mach-msm/irq.c
Gregory Beanec4d7922010-04-30 21:59:38 -07002 *
Duy Truong790f06d2013-02-13 16:38:12 -08003 * Copyright (c) 2009-2011 The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07004 * Copyright (C) 2009 Google, Inc.
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
Gregory Beanec4d7922010-04-30 21:59:38 -07009 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
Gregory Beanec4d7922010-04-30 21:59:38 -070015 */
16
17#include <linux/io.h>
18#include <linux/irq.h>
19#include <linux/interrupt.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/irqdesc.h>
Gregory Beanec4d7922010-04-30 21:59:38 -070021#include <asm/irq.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070022#include <asm/io.h>
23#include <mach/fiq.h>
24#include <mach/msm_iomap.h>
25
26#include "sirc.h"
Gregory Beanec4d7922010-04-30 21:59:38 -070027
28static unsigned int int_enable;
29static unsigned int wake_enable;
30
31static struct sirc_regs_t sirc_regs = {
32 .int_enable = SPSS_SIRC_INT_ENABLE,
33 .int_enable_clear = SPSS_SIRC_INT_ENABLE_CLEAR,
34 .int_enable_set = SPSS_SIRC_INT_ENABLE_SET,
35 .int_type = SPSS_SIRC_INT_TYPE,
36 .int_polarity = SPSS_SIRC_INT_POLARITY,
37 .int_clear = SPSS_SIRC_INT_CLEAR,
38};
39
40static struct sirc_cascade_regs sirc_reg_table[] = {
41 {
42 .int_status = SPSS_SIRC_IRQ_STATUS,
43 .cascade_irq = INT_SIRC_0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044 .cascade_fiq = INT_SIRC_1,
Gregory Beanec4d7922010-04-30 21:59:38 -070045 }
46};
47
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048static unsigned int save_type;
49static unsigned int save_polarity;
50
Gregory Beanec4d7922010-04-30 21:59:38 -070051/* Mask off the given interrupt. Keep the int_enable mask in sync with
52 the enable reg, so it can be restored after power collapse. */
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010053static void sirc_irq_mask(struct irq_data *d)
Gregory Beanec4d7922010-04-30 21:59:38 -070054{
55 unsigned int mask;
56
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010057 mask = 1 << (d->irq - FIRST_SIRC_IRQ);
Gregory Beanec4d7922010-04-30 21:59:38 -070058 writel(mask, sirc_regs.int_enable_clear);
59 int_enable &= ~mask;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060 mb();
Gregory Beanec4d7922010-04-30 21:59:38 -070061 return;
62}
63
64/* Unmask the given interrupt. Keep the int_enable mask in sync with
65 the enable reg, so it can be restored after power collapse. */
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010066static void sirc_irq_unmask(struct irq_data *d)
Gregory Beanec4d7922010-04-30 21:59:38 -070067{
68 unsigned int mask;
69
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010070 mask = 1 << (d->irq - FIRST_SIRC_IRQ);
Gregory Beanec4d7922010-04-30 21:59:38 -070071 writel(mask, sirc_regs.int_enable_set);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072 mb();
Gregory Beanec4d7922010-04-30 21:59:38 -070073 int_enable |= mask;
74 return;
75}
76
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010077static void sirc_irq_ack(struct irq_data *d)
Gregory Beanec4d7922010-04-30 21:59:38 -070078{
79 unsigned int mask;
80
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010081 mask = 1 << (d->irq - FIRST_SIRC_IRQ);
Gregory Beanec4d7922010-04-30 21:59:38 -070082 writel(mask, sirc_regs.int_clear);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083 mb();
Gregory Beanec4d7922010-04-30 21:59:38 -070084 return;
85}
86
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010087static int sirc_irq_set_wake(struct irq_data *d, unsigned int on)
Gregory Beanec4d7922010-04-30 21:59:38 -070088{
89 unsigned int mask;
90
91 /* Used to set the interrupt enable mask during power collapse. */
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +010092 mask = 1 << (d->irq - FIRST_SIRC_IRQ);
Gregory Beanec4d7922010-04-30 21:59:38 -070093 if (on)
94 wake_enable |= mask;
95 else
96 wake_enable &= ~mask;
97
98 return 0;
99}
100
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +0100101static int sirc_irq_set_type(struct irq_data *d, unsigned int flow_type)
Gregory Beanec4d7922010-04-30 21:59:38 -0700102{
103 unsigned int mask;
104 unsigned int val;
105
Lennert Buytenhek0f86ee02010-11-29 10:37:34 +0100106 mask = 1 << (d->irq - FIRST_SIRC_IRQ);
Gregory Beanec4d7922010-04-30 21:59:38 -0700107 val = readl(sirc_regs.int_polarity);
108
109 if (flow_type & (IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING))
110 val |= mask;
111 else
112 val &= ~mask;
113
114 writel(val, sirc_regs.int_polarity);
115
116 val = readl(sirc_regs.int_type);
117 if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
118 val |= mask;
Gregory Beanec4d7922010-04-30 21:59:38 -0700119 } else {
120 val &= ~mask;
Gregory Beanec4d7922010-04-30 21:59:38 -0700121 }
122
123 writel(val, sirc_regs.int_type);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124 mb();
Gregory Beanec4d7922010-04-30 21:59:38 -0700125
126 return 0;
127}
128
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129#if defined(CONFIG_MSM_FIQ_SUPPORT)
130void sirc_fiq_select(int irq, bool enable)
131{
132 uint32_t mask = 1 << (irq - FIRST_SIRC_IRQ);
133 uint32_t val;
134 unsigned long flags;
135
136 local_irq_save(flags);
137 val = readl(SPSS_SIRC_INT_SELECT);
138 if (enable)
139 val |= mask;
140 else
141 val &= ~mask;
142 writel(val, SPSS_SIRC_INT_SELECT);
143 mb();
144 local_irq_restore(flags);
145}
146#endif
147
Gregory Beanec4d7922010-04-30 21:59:38 -0700148/* Finds the pending interrupt on the passed cascade irq and redrives it */
149static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc)
150{
151 unsigned int reg = 0;
152 unsigned int sirq;
153 unsigned int status;
154
155 while ((reg < ARRAY_SIZE(sirc_reg_table)) &&
156 (sirc_reg_table[reg].cascade_irq != irq))
157 reg++;
158
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 if (reg == ARRAY_SIZE(sirc_reg_table)) {
160 printk(KERN_ERR "%s: incorrect irq %d called\n",
161 __func__, irq);
162 return;
163 }
164
Gregory Beanec4d7922010-04-30 21:59:38 -0700165 status = readl(sirc_reg_table[reg].int_status);
166 status &= SIRC_MASK;
167 if (status == 0)
168 return;
169
170 for (sirq = 0;
171 (sirq < NR_SIRC_IRQS) && ((status & (1U << sirq)) == 0);
172 sirq++)
173 ;
174 generic_handle_irq(sirq+FIRST_SIRC_IRQ);
175
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176 irq_desc_get_chip(desc)->irq_ack(irq_get_irq_data(irq));
177}
178
179void msm_sirc_enter_sleep(void)
180{
181 save_type = readl(sirc_regs.int_type);
182 save_polarity = readl(sirc_regs.int_polarity);
183 writel(wake_enable, sirc_regs.int_enable);
184 mb();
185 return;
186}
187
188void msm_sirc_exit_sleep(void)
189{
190 writel(save_type, sirc_regs.int_type);
191 writel(save_polarity, sirc_regs.int_polarity);
192 writel(int_enable, sirc_regs.int_enable);
193 mb();
194 return;
Gregory Beanec4d7922010-04-30 21:59:38 -0700195}
196
197static struct irq_chip sirc_irq_chip = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 .name = "sirc",
199 .irq_ack = sirc_irq_ack,
200 .irq_mask = sirc_irq_mask,
201 .irq_unmask = sirc_irq_unmask,
202 .irq_set_wake = sirc_irq_set_wake,
203 .irq_set_type = sirc_irq_set_type,
Gregory Beanec4d7922010-04-30 21:59:38 -0700204};
205
206void __init msm_init_sirc(void)
207{
208 int i;
209
210 int_enable = 0;
211 wake_enable = 0;
212
213 for (i = FIRST_SIRC_IRQ; i < LAST_SIRC_IRQ; i++) {
Thomas Gleixnerf38c02f2011-03-24 13:35:09 +0100214 irq_set_chip_and_handler(i, &sirc_irq_chip, handle_edge_irq);
Gregory Beanec4d7922010-04-30 21:59:38 -0700215 set_irq_flags(i, IRQF_VALID);
216 }
217
218 for (i = 0; i < ARRAY_SIZE(sirc_reg_table); i++) {
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100219 irq_set_chained_handler(sirc_reg_table[i].cascade_irq,
Gregory Beanec4d7922010-04-30 21:59:38 -0700220 sirc_irq_handler);
Thomas Gleixner6845664a2011-03-24 13:25:22 +0100221 irq_set_irq_wake(sirc_reg_table[i].cascade_irq, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222#if defined(CONFIG_MSM_FIQ_SUPPORT)
223 msm_fiq_select(sirc_reg_table[i].cascade_fiq);
224 msm_fiq_enable(sirc_reg_table[i].cascade_fiq);
225#endif
Gregory Beanec4d7922010-04-30 21:59:38 -0700226 }
227 return;
228}
229