diff options
Diffstat (limited to 'arch/arm/common/fiq_glue_setup.c')
-rw-r--r-- | arch/arm/common/fiq_glue_setup.c | 155 |
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 | |||
22 | extern unsigned char fiq_glue, fiq_glue_end; | ||
23 | extern void fiq_glue_setup(void *func, void *data, void *sp); | ||
24 | |||
25 | static struct fiq_handler fiq_debbuger_fiq_handler = { | ||
26 | .name = "fiq_glue", | ||
27 | }; | ||
28 | DEFINE_PER_CPU(void *, fiq_stack); | ||
29 | static struct fiq_glue_handler *current_handler; | ||
30 | static DEFINE_MUTEX(fiq_glue_lock); | ||
31 | |||
32 | static 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 | |||
39 | int 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 | |||
74 | err_claim_fiq: | ||
75 | err_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 | } | ||
80 | err_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 | |||
93 | void 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 | |||
103 | static 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 | |||
121 | static struct notifier_block fiq_glue_cpu_pm_notifier = { | ||
122 | .notifier_call = fiq_glue_cpu_pm_notify, | ||
123 | }; | ||
124 | |||
125 | static int __init fiq_glue_cpu_pm_init(void) | ||
126 | { | ||
127 | return cpu_pm_register_notifier(&fiq_glue_cpu_pm_notifier); | ||
128 | } | ||
129 | core_initcall(fiq_glue_cpu_pm_init); | ||
130 | |||
131 | #ifdef CONFIG_PM | ||
132 | static int fiq_glue_syscore_suspend(void) | ||
133 | { | ||
134 | local_fiq_disable(); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void fiq_glue_syscore_resume(void) | ||
139 | { | ||
140 | fiq_glue_resume(); | ||
141 | local_fiq_enable(); | ||
142 | } | ||
143 | |||
144 | static struct syscore_ops fiq_glue_syscore_ops = { | ||
145 | .suspend = fiq_glue_syscore_suspend, | ||
146 | .resume = fiq_glue_syscore_resume, | ||
147 | }; | ||
148 | |||
149 | static int __init fiq_glue_syscore_init(void) | ||
150 | { | ||
151 | register_syscore_ops(&fiq_glue_syscore_ops); | ||
152 | return 0; | ||
153 | } | ||
154 | late_initcall(fiq_glue_syscore_init); | ||
155 | #endif | ||