aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/common/fiq_glue_setup.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/common/fiq_glue_setup.c')
-rw-r--r--arch/arm/common/fiq_glue_setup.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/arch/arm/common/fiq_glue_setup.c b/arch/arm/common/fiq_glue_setup.c
new file mode 100644
index 00000000000..59586861a63
--- /dev/null
+++ b/arch/arm/common/fiq_glue_setup.c
@@ -0,0 +1,155 @@
1/*
2 * Copyright (C) 2010 Google, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/percpu.h>
16#include <linux/slab.h>
17#include <linux/syscore_ops.h>
18#include <asm/cpu_pm.h>
19#include <asm/fiq.h>
20#include <asm/fiq_glue.h>
21
22extern unsigned char fiq_glue, fiq_glue_end;
23extern void fiq_glue_setup(void *func, void *data, void *sp);
24
25static struct fiq_handler fiq_debbuger_fiq_handler = {
26 .name = "fiq_glue",
27};
28DEFINE_PER_CPU(void *, fiq_stack);
29static struct fiq_glue_handler *current_handler;
30static DEFINE_MUTEX(fiq_glue_lock);
31
32static void fiq_glue_setup_helper(void *info)
33{
34 struct fiq_glue_handler *handler = info;
35 fiq_glue_setup(handler->fiq, handler,
36 __get_cpu_var(fiq_stack) + THREAD_START_SP);
37}
38
39int fiq_glue_register_handler(struct fiq_glue_handler *handler)
40{
41 int ret;
42 int cpu;
43
44 if (!handler || !handler->fiq)
45 return -EINVAL;
46
47 mutex_lock(&fiq_glue_lock);
48 if (fiq_stack) {
49 ret = -EBUSY;
50 goto err_busy;
51 }
52
53 for_each_possible_cpu(cpu) {
54 void *stack;
55 stack = (void *)__get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER);
56 if (WARN_ON(!stack)) {
57 ret = -ENOMEM;
58 goto err_alloc_fiq_stack;
59 }
60 per_cpu(fiq_stack, cpu) = stack;
61 }
62
63 ret = claim_fiq(&fiq_debbuger_fiq_handler);
64 if (WARN_ON(ret))
65 goto err_claim_fiq;
66
67 current_handler = handler;
68 on_each_cpu(fiq_glue_setup_helper, handler, true);
69 set_fiq_handler(&fiq_glue, &fiq_glue_end - &fiq_glue);
70
71 mutex_unlock(&fiq_glue_lock);
72 return 0;
73
74err_claim_fiq:
75err_alloc_fiq_stack:
76 for_each_possible_cpu(cpu) {
77 __free_pages(per_cpu(fiq_stack, cpu), THREAD_SIZE_ORDER);
78 per_cpu(fiq_stack, cpu) = NULL;
79 }
80err_busy:
81 mutex_unlock(&fiq_glue_lock);
82 return ret;
83}
84
85/**
86 * fiq_glue_resume - Restore fiqs after suspend or low power idle states
87 *
88 * This must be called before calling local_fiq_enable after returning from a
89 * power state where the fiq mode registers were lost. If a driver provided
90 * a resume hook when it registered the handler it will be called.
91 */
92
93void fiq_glue_resume(void)
94{
95 if (!current_handler)
96 return;
97 fiq_glue_setup(current_handler->fiq, current_handler,
98 __get_cpu_var(fiq_stack) + THREAD_START_SP);
99 if (current_handler->resume)
100 current_handler->resume(current_handler);
101}
102
103static int fiq_glue_cpu_pm_notify(struct notifier_block *self, unsigned long cmd,
104 void *v)
105{
106 switch (cmd) {
107 case CPU_PM_ENTER:
108 //pr_info("cpu pm enter %d\n", smp_processor_id());
109 local_fiq_disable();
110 break;
111 case CPU_PM_ENTER_FAILED:
112 case CPU_PM_EXIT:
113 fiq_glue_resume();
114 local_fiq_enable();
115 //pr_info("cpu pm exit %d\n", smp_processor_id());
116 break;
117 }
118 return NOTIFY_OK;
119}
120
121static struct notifier_block fiq_glue_cpu_pm_notifier = {
122 .notifier_call = fiq_glue_cpu_pm_notify,
123};
124
125static int __init fiq_glue_cpu_pm_init(void)
126{
127 return cpu_pm_register_notifier(&fiq_glue_cpu_pm_notifier);
128}
129core_initcall(fiq_glue_cpu_pm_init);
130
131#ifdef CONFIG_PM
132static int fiq_glue_syscore_suspend(void)
133{
134 local_fiq_disable();
135 return 0;
136}
137
138static void fiq_glue_syscore_resume(void)
139{
140 fiq_glue_resume();
141 local_fiq_enable();
142}
143
144static struct syscore_ops fiq_glue_syscore_ops = {
145 .suspend = fiq_glue_syscore_suspend,
146 .resume = fiq_glue_syscore_resume,
147};
148
149static int __init fiq_glue_syscore_init(void)
150{
151 register_syscore_ops(&fiq_glue_syscore_ops);
152 return 0;
153}
154late_initcall(fiq_glue_syscore_init);
155#endif