1 /*
2  * arch/arm/mach-tegra/legacy_irq.c
3  *
4  * Copyright (C) 2010 Google, Inc.
5  * Author: Colin Cross <ccross@android.com>
6  *
7  * This software is licensed under the terms of the GNU General Public
8  * License version 2, as published by the Free Software Foundation, and
9  * may be copied, distributed, and modified under those terms.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  */
17 
18 #include <linux/io.h>
19 #include <linux/kernel.h>
20 #include <mach/iomap.h>
21 #include <mach/irqs.h>
22 #include <mach/legacy_irq.h>
23 
24 #define INT_SYS_NR	(INT_GPIO_BASE - INT_PRI_BASE)
25 #define INT_SYS_SZ	(INT_SEC_BASE - INT_PRI_BASE)
26 #define PPI_NR		((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ)
27 
28 #define ICTLR_CPU_IEP_VFIQ	0x08
29 #define ICTLR_CPU_IEP_FIR	0x14
30 #define ICTLR_CPU_IEP_FIR_SET	0x18
31 #define ICTLR_CPU_IEP_FIR_CLR	0x1c
32 
33 #define ICTLR_CPU_IER		0x20
34 #define ICTLR_CPU_IER_SET	0x24
35 #define ICTLR_CPU_IER_CLR	0x28
36 #define ICTLR_CPU_IEP_CLASS	0x2C
37 
38 #define ICTLR_COP_IER		0x30
39 #define ICTLR_COP_IER_SET	0x34
40 #define ICTLR_COP_IER_CLR	0x38
41 #define ICTLR_COP_IEP_CLASS	0x3c
42 
43 #define NUM_ICTLRS 4
44 
45 static void __iomem *ictlr_reg_base[] = {
46 	IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE),
47 	IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
48 	IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE),
49 	IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
50 };
51 
52 static u32 tegra_legacy_wake_mask[4];
53 static u32 tegra_legacy_saved_mask[4];
54 
55 /* When going into deep sleep, the CPU is powered down, taking the GIC with it
56    In order to wake, the wake interrupts need to be enabled in the legacy
57    interrupt controller. */
tegra_legacy_unmask_irq(unsigned int irq)58 void tegra_legacy_unmask_irq(unsigned int irq)
59 {
60 	void __iomem *base;
61 	pr_debug("%s: %d\n", __func__, irq);
62 
63 	irq -= 32;
64 	base = ictlr_reg_base[irq>>5];
65 	writel(1 << (irq & 31), base + ICTLR_CPU_IER_SET);
66 }
67 
tegra_legacy_mask_irq(unsigned int irq)68 void tegra_legacy_mask_irq(unsigned int irq)
69 {
70 	void __iomem *base;
71 	pr_debug("%s: %d\n", __func__, irq);
72 
73 	irq -= 32;
74 	base = ictlr_reg_base[irq>>5];
75 	writel(1 << (irq & 31), base + ICTLR_CPU_IER_CLR);
76 }
77 
tegra_legacy_force_irq_set(unsigned int irq)78 void tegra_legacy_force_irq_set(unsigned int irq)
79 {
80 	void __iomem *base;
81 	pr_debug("%s: %d\n", __func__, irq);
82 
83 	irq -= 32;
84 	base = ictlr_reg_base[irq>>5];
85 	writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_SET);
86 }
87 
tegra_legacy_force_irq_clr(unsigned int irq)88 void tegra_legacy_force_irq_clr(unsigned int irq)
89 {
90 	void __iomem *base;
91 	pr_debug("%s: %d\n", __func__, irq);
92 
93 	irq -= 32;
94 	base = ictlr_reg_base[irq>>5];
95 	writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_CLR);
96 }
97 
tegra_legacy_force_irq_status(unsigned int irq)98 int tegra_legacy_force_irq_status(unsigned int irq)
99 {
100 	void __iomem *base;
101 	pr_debug("%s: %d\n", __func__, irq);
102 
103 	irq -= 32;
104 	base = ictlr_reg_base[irq>>5];
105 	return !!(readl(base + ICTLR_CPU_IEP_FIR) & (1 << (irq & 31)));
106 }
107 
tegra_legacy_select_fiq(unsigned int irq,bool fiq)108 void tegra_legacy_select_fiq(unsigned int irq, bool fiq)
109 {
110 	void __iomem *base;
111 	pr_debug("%s: %d\n", __func__, irq);
112 
113 	irq -= 32;
114 	base = ictlr_reg_base[irq>>5];
115 	writel(fiq << (irq & 31), base + ICTLR_CPU_IEP_CLASS);
116 }
117 
tegra_legacy_vfiq(int nr)118 unsigned long tegra_legacy_vfiq(int nr)
119 {
120 	void __iomem *base;
121 	base = ictlr_reg_base[nr];
122 	return readl(base + ICTLR_CPU_IEP_VFIQ);
123 }
124 
tegra_legacy_class(int nr)125 unsigned long tegra_legacy_class(int nr)
126 {
127 	void __iomem *base;
128 	base = ictlr_reg_base[nr];
129 	return readl(base + ICTLR_CPU_IEP_CLASS);
130 }
131 
tegra_legacy_irq_set_wake(int irq,int enable)132 int tegra_legacy_irq_set_wake(int irq, int enable)
133 {
134 	irq -= 32;
135 	if (enable)
136 		tegra_legacy_wake_mask[irq >> 5] |= 1 << (irq & 31);
137 	else
138 		tegra_legacy_wake_mask[irq >> 5] &= ~(1 << (irq & 31));
139 
140 	return 0;
141 }
142 
tegra_legacy_irq_set_lp1_wake_mask(void)143 void tegra_legacy_irq_set_lp1_wake_mask(void)
144 {
145 	void __iomem *base;
146 	int i;
147 
148 	for (i = 0; i < NUM_ICTLRS; i++) {
149 		base = ictlr_reg_base[i];
150 		tegra_legacy_saved_mask[i] = readl(base + ICTLR_CPU_IER);
151 		writel(tegra_legacy_wake_mask[i], base + ICTLR_CPU_IER);
152 	}
153 }
154 
tegra_legacy_irq_restore_mask(void)155 void tegra_legacy_irq_restore_mask(void)
156 {
157 	void __iomem *base;
158 	int i;
159 
160 	for (i = 0; i < NUM_ICTLRS; i++) {
161 		base = ictlr_reg_base[i];
162 		writel(tegra_legacy_saved_mask[i], base + ICTLR_CPU_IER);
163 	}
164 }
165 
tegra_init_legacy_irq(void)166 void tegra_init_legacy_irq(void)
167 {
168 	int i;
169 
170 	for (i = 0; i < NUM_ICTLRS; i++) {
171 		void __iomem *ictlr = ictlr_reg_base[i];
172 		writel(~0, ictlr + ICTLR_CPU_IER_CLR);
173 		writel(0, ictlr + ICTLR_CPU_IEP_CLASS);
174 	}
175 }
176 
177 #ifdef CONFIG_PM
178 static u32 cop_ier[NUM_ICTLRS];
179 static u32 cpu_ier[NUM_ICTLRS];
180 static u32 cpu_iep[NUM_ICTLRS];
181 
tegra_irq_suspend(void)182 void tegra_irq_suspend(void)
183 {
184 	unsigned long flags;
185 	int i;
186 
187 	local_irq_save(flags);
188 	for (i = 0; i < NUM_ICTLRS; i++) {
189 		void __iomem *ictlr = ictlr_reg_base[i];
190 		cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
191 		cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
192 		cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
193 		writel(~0, ictlr + ICTLR_COP_IER_CLR);
194 	}
195 	local_irq_restore(flags);
196 }
197 
tegra_irq_resume(void)198 void tegra_irq_resume(void)
199 {
200 	unsigned long flags;
201 	int i;
202 
203 	local_irq_save(flags);
204 	for (i = 0; i < NUM_ICTLRS; i++) {
205 		void __iomem *ictlr = ictlr_reg_base[i];
206 		writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
207 		writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
208 		writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
209 		writel(0, ictlr + ICTLR_COP_IEP_CLASS);
210 		writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
211 		writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
212 	}
213 	local_irq_restore(flags);
214 }
215 #endif
216