2 * Copyright (C) 2010 Google, Inc.
5 * Colin Cross <ccross@google.com>
7 * Copyright (C) 2010, NVIDIA Corporation
9 * This software is licensed under the terms of the GNU General Public
10 * License version 2, as published by the Free Software Foundation, and
11 * may be copied, distributed, and modified under those terms.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
20 #include <linux/kernel.h>
21 #include <linux/delay.h>
22 #include <linux/debugfs.h>
23 #include <linux/init.h>
24 #include <linux/interrupt.h>
25 #include <linux/irq.h>
27 #include <linux/seq_file.h>
29 #include <asm/hardware/gic.h>
31 #include <mach/iomap.h>
32 #include <mach/legacy_irq.h>
33 #include <mach/suspend.h>
38 #define PMC_CTRL_LATCH_WAKEUPS (1 << 5)
39 #define PMC_WAKE_MASK 0xc
40 #define PMC_WAKE_LEVEL 0x10
41 #define PMC_WAKE_STATUS 0x14
42 #define PMC_SW_WAKE_STATUS 0x18
43 #define PMC_DPD_SAMPLE 0x20
45 static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
47 static u32 tegra_lp0_wake_enb;
48 static u32 tegra_lp0_wake_level;
49 static u32 tegra_lp0_wake_level_any;
51 static unsigned int tegra_wake_irq_count[32];
53 /* ensures that sufficient time is passed for a register write to
54 * serialize into the 32KHz domain */
55 static void pmc_32kwritel(u32 val, unsigned long offs)
57 writel(val, pmc + offs);
61 int tegra_set_lp0_wake(int irq, int enable)
63 int wake = tegra_irq_to_wake(irq);
69 tegra_lp0_wake_enb |= 1 << wake;
71 tegra_lp0_wake_enb &= ~(1 << wake);
76 int tegra_set_lp0_wake_type(int irq, int flow_type)
78 int wake = tegra_irq_to_wake(irq);
84 case IRQF_TRIGGER_FALLING:
85 case IRQF_TRIGGER_LOW:
86 tegra_lp0_wake_level &= ~(1 << wake);
87 tegra_lp0_wake_level_any &= ~(1 << wake);
89 case IRQF_TRIGGER_HIGH:
90 case IRQF_TRIGGER_RISING:
91 tegra_lp0_wake_level |= 1 << wake;
92 tegra_lp0_wake_level_any &= ~(1 << wake);
95 case IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING:
96 tegra_lp0_wake_level_any |= 1 << wake;
106 int tegra_set_lp1_wake(int irq, int enable)
108 return tegra_legacy_irq_set_wake(irq, enable);
111 void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any)
117 wake_level &= wake_enb;
118 wake_any &= wake_enb;
120 wake_level |= (tegra_lp0_wake_level & tegra_lp0_wake_enb);
121 wake_any |= (tegra_lp0_wake_level_any & tegra_lp0_wake_enb);
123 wake_enb |= tegra_lp0_wake_enb;
125 pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
126 temp = readl(pmc + PMC_CTRL);
127 temp |= PMC_CTRL_LATCH_WAKEUPS;
128 pmc_32kwritel(temp, PMC_CTRL);
129 temp &= ~PMC_CTRL_LATCH_WAKEUPS;
130 pmc_32kwritel(temp, PMC_CTRL);
131 status = readl(pmc + PMC_SW_WAKE_STATUS);
132 lvl = readl(pmc + PMC_WAKE_LEVEL);
134 /* flip the wakeup trigger for any-edge triggered pads
135 * which are currently asserting as wakeups */
141 writel(wake_level, pmc + PMC_WAKE_LEVEL);
142 /* Enable DPD sample to trigger sampling pads data and direction
143 * in which pad will be driven during lp0 mode*/
144 writel(0x1, pmc + PMC_DPD_SAMPLE);
146 writel(wake_enb, pmc + PMC_WAKE_MASK);
149 static void tegra_irq_handle_wake(void)
153 struct irq_desc *desc;
155 unsigned long wake_status = readl(pmc + PMC_WAKE_STATUS);
156 for_each_set_bit(wake, &wake_status, sizeof(wake_status) * 8) {
157 irq = tegra_wake_to_irq(wake);
159 pr_info("Resume caused by WAKE%d\n", wake);
163 desc = irq_to_desc(irq);
164 if (!desc || !desc->action || !desc->action->name) {
165 pr_info("Resume caused by WAKE%d, irq %d\n", wake, irq);
169 pr_info("Resume caused by WAKE%d, %s\n", wake,
172 tegra_wake_irq_count[wake]++;
174 generic_handle_irq(irq);
178 static void tegra_mask(unsigned int irq)
181 tegra_legacy_mask_irq(irq);
184 static void tegra_unmask(unsigned int irq)
187 tegra_legacy_unmask_irq(irq);
190 static int tegra_set_wake(unsigned int irq, unsigned int enable)
193 ret = tegra_set_lp1_wake(irq, enable);
197 if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
198 return tegra_set_lp0_wake(irq, enable);
203 static int tegra_set_type(unsigned int irq, unsigned int flow_type)
205 if (tegra_get_suspend_mode() == TEGRA_SUSPEND_LP0)
206 return tegra_set_lp0_wake_type(irq, flow_type);
211 static void tegra_ack(unsigned int irq)
213 tegra_legacy_force_irq_clr(irq);
217 static int tegra_retrigger(unsigned int irq)
219 tegra_legacy_force_irq_set(irq);
223 static struct irq_chip tegra_irq = {
227 .unmask = tegra_unmask,
228 .set_wake = tegra_set_wake,
229 .set_type = tegra_set_type,
231 .set_affinity = gic_set_cpu,
233 .retrigger = tegra_retrigger,
236 void __init tegra_init_irq(void)
241 tegra_init_legacy_irq();
243 gic_dist_init(0, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 29);
244 gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
246 for (i = 0; i < INT_MAIN_NR; i++) {
247 irq = INT_PRI_BASE + i;
248 set_irq_chip(irq, &tegra_irq);
249 set_irq_handler(irq, handle_level_irq);
250 set_irq_flags(irq, IRQF_VALID);
254 void tegra_irq_suspend(void)
256 tegra_legacy_irq_suspend();
259 void tegra_irq_resume(void)
261 tegra_legacy_irq_resume();
262 tegra_irq_handle_wake();
265 #ifdef CONFIG_DEBUG_FS
266 static int tegra_wake_irq_debug_show(struct seq_file *s, void *data)
270 struct irq_desc *desc;
271 const char *irq_name;
273 seq_printf(s, "wake irq count name\n");
274 seq_printf(s, "----------------------\n");
275 for (wake = 0; wake < 32; wake++) {
276 irq = tegra_wake_to_irq(wake);
280 desc = irq_to_desc(irq);
281 if (tegra_wake_irq_count[wake] == 0 && desc->action == NULL)
284 if (!(desc->status & IRQ_WAKEUP))
287 irq_name = (desc->action && desc->action->name) ?
288 desc->action->name : "???";
290 seq_printf(s, "%4d %3d %5d %s\n",
291 wake, irq, tegra_wake_irq_count[wake], irq_name);
296 static int tegra_wake_irq_debug_open(struct inode *inode, struct file *file)
298 return single_open(file, tegra_wake_irq_debug_show, NULL);
301 static const struct file_operations tegra_wake_irq_debug_fops = {
302 .open = tegra_wake_irq_debug_open,
305 .release = single_release,
308 static int __init tegra_irq_debug_init(void)
312 d = debugfs_create_file("wake_irq", 0755, NULL, NULL,
313 &tegra_wake_irq_debug_fops);
315 pr_info("Failed to create suspend_mode debug file\n");
322 late_initcall(tegra_irq_debug_init);