diff options
Diffstat (limited to 'arch/blackfin/kernel')
-rw-r--r-- | arch/blackfin/kernel/Makefile | 14 | ||||
-rw-r--r-- | arch/blackfin/kernel/asm-offsets.c | 136 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_dma_5xx.c | 742 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_gpio.c | 637 | ||||
-rw-r--r-- | arch/blackfin/kernel/bfin_ksyms.c | 119 | ||||
-rw-r--r-- | arch/blackfin/kernel/dma-mapping.c | 183 | ||||
-rw-r--r-- | arch/blackfin/kernel/dualcore_test.c | 49 | ||||
-rw-r--r-- | arch/blackfin/kernel/entry.S | 94 | ||||
-rw-r--r-- | arch/blackfin/kernel/flat.c | 101 | ||||
-rw-r--r-- | arch/blackfin/kernel/init_task.c | 60 | ||||
-rw-r--r-- | arch/blackfin/kernel/irqchip.c | 147 | ||||
-rw-r--r-- | arch/blackfin/kernel/module.c | 429 | ||||
-rw-r--r-- | arch/blackfin/kernel/process.c | 394 | ||||
-rw-r--r-- | arch/blackfin/kernel/ptrace.c | 430 | ||||
-rw-r--r-- | arch/blackfin/kernel/setup.c | 902 | ||||
-rw-r--r-- | arch/blackfin/kernel/signal.c | 356 | ||||
-rw-r--r-- | arch/blackfin/kernel/sys_bfin.c | 115 | ||||
-rw-r--r-- | arch/blackfin/kernel/time.c | 326 | ||||
-rw-r--r-- | arch/blackfin/kernel/traps.c | 649 | ||||
-rw-r--r-- | arch/blackfin/kernel/vmlinux.lds.S | 228 |
20 files changed, 6111 insertions, 0 deletions
diff --git a/arch/blackfin/kernel/Makefile b/arch/blackfin/kernel/Makefile new file mode 100644 index 000000000000..f3b7d2f9d49c --- /dev/null +++ b/arch/blackfin/kernel/Makefile | |||
@@ -0,0 +1,14 @@ | |||
1 | # | ||
2 | # arch/blackfin/kernel/Makefile | ||
3 | # | ||
4 | |||
5 | extra-y := init_task.o vmlinux.lds | ||
6 | |||
7 | obj-y := \ | ||
8 | entry.o process.o bfin_ksyms.o ptrace.o setup.o signal.o \ | ||
9 | sys_bfin.o time.o traps.o irqchip.o dma-mapping.o bfin_gpio.o \ | ||
10 | flat.o | ||
11 | |||
12 | obj-$(CONFIG_MODULES) += module.o | ||
13 | obj-$(CONFIG_BFIN_DMA_5XX) += bfin_dma_5xx.o | ||
14 | obj-$(CONFIG_DUAL_CORE_TEST_MODULE) += dualcore_test.o | ||
diff --git a/arch/blackfin/kernel/asm-offsets.c b/arch/blackfin/kernel/asm-offsets.c new file mode 100644 index 000000000000..41d9a9f89700 --- /dev/null +++ b/arch/blackfin/kernel/asm-offsets.c | |||
@@ -0,0 +1,136 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/asm-offsets.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: generate definitions needed by assembly language modules. | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/stddef.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/kernel_stat.h> | ||
33 | #include <linux/ptrace.h> | ||
34 | #include <linux/hardirq.h> | ||
35 | #include <asm/irq.h> | ||
36 | #include <asm/thread_info.h> | ||
37 | |||
38 | #define DEFINE(sym, val) \ | ||
39 | asm volatile("\n->" #sym " %0 " #val : : "i" (val)) | ||
40 | |||
41 | int main(void) | ||
42 | { | ||
43 | /* offsets into the task struct */ | ||
44 | DEFINE(TASK_STATE, offsetof(struct task_struct, state)); | ||
45 | DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); | ||
46 | DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); | ||
47 | DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); | ||
48 | DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); | ||
49 | DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info)); | ||
50 | DEFINE(TASK_MM, offsetof(struct task_struct, mm)); | ||
51 | DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); | ||
52 | DEFINE(TASK_SIGPENDING, offsetof(struct task_struct, pending)); | ||
53 | |||
54 | /* offsets into the irq_cpustat_t struct */ | ||
55 | DEFINE(CPUSTAT_SOFTIRQ_PENDING, | ||
56 | offsetof(irq_cpustat_t, __softirq_pending)); | ||
57 | |||
58 | /* offsets into the thread struct */ | ||
59 | DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); | ||
60 | DEFINE(THREAD_USP, offsetof(struct thread_struct, usp)); | ||
61 | DEFINE(THREAD_SR, offsetof(struct thread_struct, seqstat)); | ||
62 | DEFINE(PT_SR, offsetof(struct thread_struct, seqstat)); | ||
63 | DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0)); | ||
64 | DEFINE(THREAD_PC, offsetof(struct thread_struct, pc)); | ||
65 | DEFINE(KERNEL_STACK_SIZE, THREAD_SIZE); | ||
66 | |||
67 | /* offsets into the pt_regs */ | ||
68 | DEFINE(PT_ORIG_P0, offsetof(struct pt_regs, orig_p0)); | ||
69 | DEFINE(PT_ORIG_PC, offsetof(struct pt_regs, orig_pc)); | ||
70 | DEFINE(PT_R0, offsetof(struct pt_regs, r0)); | ||
71 | DEFINE(PT_R1, offsetof(struct pt_regs, r1)); | ||
72 | DEFINE(PT_R2, offsetof(struct pt_regs, r2)); | ||
73 | DEFINE(PT_R3, offsetof(struct pt_regs, r3)); | ||
74 | DEFINE(PT_R4, offsetof(struct pt_regs, r4)); | ||
75 | DEFINE(PT_R5, offsetof(struct pt_regs, r5)); | ||
76 | DEFINE(PT_R6, offsetof(struct pt_regs, r6)); | ||
77 | DEFINE(PT_R7, offsetof(struct pt_regs, r7)); | ||
78 | |||
79 | DEFINE(PT_P0, offsetof(struct pt_regs, p0)); | ||
80 | DEFINE(PT_P1, offsetof(struct pt_regs, p1)); | ||
81 | DEFINE(PT_P2, offsetof(struct pt_regs, p2)); | ||
82 | DEFINE(PT_P3, offsetof(struct pt_regs, p3)); | ||
83 | DEFINE(PT_P4, offsetof(struct pt_regs, p4)); | ||
84 | DEFINE(PT_P5, offsetof(struct pt_regs, p5)); | ||
85 | |||
86 | DEFINE(PT_FP, offsetof(struct pt_regs, fp)); | ||
87 | DEFINE(PT_USP, offsetof(struct pt_regs, usp)); | ||
88 | DEFINE(PT_I0, offsetof(struct pt_regs, i0)); | ||
89 | DEFINE(PT_I1, offsetof(struct pt_regs, i1)); | ||
90 | DEFINE(PT_I2, offsetof(struct pt_regs, i2)); | ||
91 | DEFINE(PT_I3, offsetof(struct pt_regs, i3)); | ||
92 | DEFINE(PT_M0, offsetof(struct pt_regs, m0)); | ||
93 | DEFINE(PT_M1, offsetof(struct pt_regs, m1)); | ||
94 | DEFINE(PT_M2, offsetof(struct pt_regs, m2)); | ||
95 | DEFINE(PT_M3, offsetof(struct pt_regs, m3)); | ||
96 | DEFINE(PT_L0, offsetof(struct pt_regs, l0)); | ||
97 | DEFINE(PT_L1, offsetof(struct pt_regs, l1)); | ||
98 | DEFINE(PT_L2, offsetof(struct pt_regs, l2)); | ||
99 | DEFINE(PT_L3, offsetof(struct pt_regs, l3)); | ||
100 | DEFINE(PT_B0, offsetof(struct pt_regs, b0)); | ||
101 | DEFINE(PT_B1, offsetof(struct pt_regs, b1)); | ||
102 | DEFINE(PT_B2, offsetof(struct pt_regs, b2)); | ||
103 | DEFINE(PT_B3, offsetof(struct pt_regs, b3)); | ||
104 | DEFINE(PT_A0X, offsetof(struct pt_regs, a0x)); | ||
105 | DEFINE(PT_A0W, offsetof(struct pt_regs, a0w)); | ||
106 | DEFINE(PT_A1X, offsetof(struct pt_regs, a1x)); | ||
107 | DEFINE(PT_A1W, offsetof(struct pt_regs, a1w)); | ||
108 | DEFINE(PT_LC0, offsetof(struct pt_regs, lc0)); | ||
109 | DEFINE(PT_LC1, offsetof(struct pt_regs, lc1)); | ||
110 | DEFINE(PT_LT0, offsetof(struct pt_regs, lt0)); | ||
111 | DEFINE(PT_LT1, offsetof(struct pt_regs, lt1)); | ||
112 | DEFINE(PT_LB0, offsetof(struct pt_regs, lb0)); | ||
113 | DEFINE(PT_LB1, offsetof(struct pt_regs, lb1)); | ||
114 | DEFINE(PT_ASTAT, offsetof(struct pt_regs, astat)); | ||
115 | DEFINE(PT_RESERVED, offsetof(struct pt_regs, reserved)); | ||
116 | DEFINE(PT_RETS, offsetof(struct pt_regs, rets)); | ||
117 | DEFINE(PT_PC, offsetof(struct pt_regs, pc)); | ||
118 | DEFINE(PT_RETX, offsetof(struct pt_regs, retx)); | ||
119 | DEFINE(PT_RETN, offsetof(struct pt_regs, retn)); | ||
120 | DEFINE(PT_RETE, offsetof(struct pt_regs, rete)); | ||
121 | DEFINE(PT_SEQSTAT, offsetof(struct pt_regs, seqstat)); | ||
122 | DEFINE(PT_SYSCFG, offsetof(struct pt_regs, syscfg)); | ||
123 | DEFINE(PT_IPEND, offsetof(struct pt_regs, ipend)); | ||
124 | DEFINE(SIZEOF_PTREGS, sizeof(struct pt_regs)); | ||
125 | DEFINE(PT_TEXT_ADDR, sizeof(struct pt_regs)); /* Needed by gdb */ | ||
126 | DEFINE(PT_TEXT_END_ADDR, 4 + sizeof(struct pt_regs));/* Needed by gdb */ | ||
127 | DEFINE(PT_DATA_ADDR, 8 + sizeof(struct pt_regs)); /* Needed by gdb */ | ||
128 | DEFINE(PT_FDPIC_EXEC, 12 + sizeof(struct pt_regs)); /* Needed by gdb */ | ||
129 | DEFINE(PT_FDPIC_INTERP, 16 + sizeof(struct pt_regs));/* Needed by gdb */ | ||
130 | |||
131 | /* signal defines */ | ||
132 | DEFINE(SIGSEGV, SIGSEGV); | ||
133 | DEFINE(SIGTRAP, SIGTRAP); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c new file mode 100644 index 000000000000..8ea079ebecb5 --- /dev/null +++ b/arch/blackfin/kernel/bfin_dma_5xx.c | |||
@@ -0,0 +1,742 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/bfin_dma_5xx.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: This file contains the simple DMA Implementation for Blackfin | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/errno.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/sched.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <linux/param.h> | ||
36 | |||
37 | #include <asm/dma.h> | ||
38 | #include <asm/cacheflush.h> | ||
39 | |||
40 | /* Remove unused code not exported by symbol or internally called */ | ||
41 | #define REMOVE_DEAD_CODE | ||
42 | |||
43 | /************************************************************************** | ||
44 | * Global Variables | ||
45 | ***************************************************************************/ | ||
46 | |||
47 | static struct dma_channel dma_ch[MAX_BLACKFIN_DMA_CHANNEL]; | ||
48 | #if defined (CONFIG_BF561) | ||
49 | static struct dma_register *base_addr[MAX_BLACKFIN_DMA_CHANNEL] = { | ||
50 | (struct dma_register *) DMA1_0_NEXT_DESC_PTR, | ||
51 | (struct dma_register *) DMA1_1_NEXT_DESC_PTR, | ||
52 | (struct dma_register *) DMA1_2_NEXT_DESC_PTR, | ||
53 | (struct dma_register *) DMA1_3_NEXT_DESC_PTR, | ||
54 | (struct dma_register *) DMA1_4_NEXT_DESC_PTR, | ||
55 | (struct dma_register *) DMA1_5_NEXT_DESC_PTR, | ||
56 | (struct dma_register *) DMA1_6_NEXT_DESC_PTR, | ||
57 | (struct dma_register *) DMA1_7_NEXT_DESC_PTR, | ||
58 | (struct dma_register *) DMA1_8_NEXT_DESC_PTR, | ||
59 | (struct dma_register *) DMA1_9_NEXT_DESC_PTR, | ||
60 | (struct dma_register *) DMA1_10_NEXT_DESC_PTR, | ||
61 | (struct dma_register *) DMA1_11_NEXT_DESC_PTR, | ||
62 | (struct dma_register *) DMA2_0_NEXT_DESC_PTR, | ||
63 | (struct dma_register *) DMA2_1_NEXT_DESC_PTR, | ||
64 | (struct dma_register *) DMA2_2_NEXT_DESC_PTR, | ||
65 | (struct dma_register *) DMA2_3_NEXT_DESC_PTR, | ||
66 | (struct dma_register *) DMA2_4_NEXT_DESC_PTR, | ||
67 | (struct dma_register *) DMA2_5_NEXT_DESC_PTR, | ||
68 | (struct dma_register *) DMA2_6_NEXT_DESC_PTR, | ||
69 | (struct dma_register *) DMA2_7_NEXT_DESC_PTR, | ||
70 | (struct dma_register *) DMA2_8_NEXT_DESC_PTR, | ||
71 | (struct dma_register *) DMA2_9_NEXT_DESC_PTR, | ||
72 | (struct dma_register *) DMA2_10_NEXT_DESC_PTR, | ||
73 | (struct dma_register *) DMA2_11_NEXT_DESC_PTR, | ||
74 | (struct dma_register *) MDMA1_D0_NEXT_DESC_PTR, | ||
75 | (struct dma_register *) MDMA1_S0_NEXT_DESC_PTR, | ||
76 | (struct dma_register *) MDMA1_D1_NEXT_DESC_PTR, | ||
77 | (struct dma_register *) MDMA1_S1_NEXT_DESC_PTR, | ||
78 | (struct dma_register *) MDMA2_D0_NEXT_DESC_PTR, | ||
79 | (struct dma_register *) MDMA2_S0_NEXT_DESC_PTR, | ||
80 | (struct dma_register *) MDMA2_D1_NEXT_DESC_PTR, | ||
81 | (struct dma_register *) MDMA2_S1_NEXT_DESC_PTR, | ||
82 | (struct dma_register *) IMDMA_D0_NEXT_DESC_PTR, | ||
83 | (struct dma_register *) IMDMA_S0_NEXT_DESC_PTR, | ||
84 | (struct dma_register *) IMDMA_D1_NEXT_DESC_PTR, | ||
85 | (struct dma_register *) IMDMA_S1_NEXT_DESC_PTR, | ||
86 | }; | ||
87 | #else | ||
88 | static struct dma_register *base_addr[MAX_BLACKFIN_DMA_CHANNEL] = { | ||
89 | (struct dma_register *) DMA0_NEXT_DESC_PTR, | ||
90 | (struct dma_register *) DMA1_NEXT_DESC_PTR, | ||
91 | (struct dma_register *) DMA2_NEXT_DESC_PTR, | ||
92 | (struct dma_register *) DMA3_NEXT_DESC_PTR, | ||
93 | (struct dma_register *) DMA4_NEXT_DESC_PTR, | ||
94 | (struct dma_register *) DMA5_NEXT_DESC_PTR, | ||
95 | (struct dma_register *) DMA6_NEXT_DESC_PTR, | ||
96 | (struct dma_register *) DMA7_NEXT_DESC_PTR, | ||
97 | #if (defined(CONFIG_BF537) || defined(CONFIG_BF534) || defined(CONFIG_BF536)) | ||
98 | (struct dma_register *) DMA8_NEXT_DESC_PTR, | ||
99 | (struct dma_register *) DMA9_NEXT_DESC_PTR, | ||
100 | (struct dma_register *) DMA10_NEXT_DESC_PTR, | ||
101 | (struct dma_register *) DMA11_NEXT_DESC_PTR, | ||
102 | #endif | ||
103 | (struct dma_register *) MDMA_D0_NEXT_DESC_PTR, | ||
104 | (struct dma_register *) MDMA_S0_NEXT_DESC_PTR, | ||
105 | (struct dma_register *) MDMA_D1_NEXT_DESC_PTR, | ||
106 | (struct dma_register *) MDMA_S1_NEXT_DESC_PTR, | ||
107 | }; | ||
108 | #endif | ||
109 | |||
110 | /*------------------------------------------------------------------------------ | ||
111 | * Set the Buffer Clear bit in the Configuration register of specific DMA | ||
112 | * channel. This will stop the descriptor based DMA operation. | ||
113 | *-----------------------------------------------------------------------------*/ | ||
114 | static void clear_dma_buffer(unsigned int channel) | ||
115 | { | ||
116 | dma_ch[channel].regs->cfg |= RESTART; | ||
117 | SSYNC(); | ||
118 | dma_ch[channel].regs->cfg &= ~RESTART; | ||
119 | SSYNC(); | ||
120 | } | ||
121 | |||
122 | int __init blackfin_dma_init(void) | ||
123 | { | ||
124 | int i; | ||
125 | |||
126 | printk(KERN_INFO "Blackfin DMA Controller\n"); | ||
127 | |||
128 | for (i = 0; i < MAX_BLACKFIN_DMA_CHANNEL; i++) { | ||
129 | dma_ch[i].chan_status = DMA_CHANNEL_FREE; | ||
130 | dma_ch[i].regs = base_addr[i]; | ||
131 | mutex_init(&(dma_ch[i].dmalock)); | ||
132 | } | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | arch_initcall(blackfin_dma_init); | ||
138 | |||
139 | /* | ||
140 | * Form the channel find the irq number for that channel. | ||
141 | */ | ||
142 | #if !defined(CONFIG_BF561) | ||
143 | |||
144 | static int bf533_channel2irq(unsigned int channel) | ||
145 | { | ||
146 | int ret_irq = -1; | ||
147 | |||
148 | switch (channel) { | ||
149 | case CH_PPI: | ||
150 | ret_irq = IRQ_PPI; | ||
151 | break; | ||
152 | |||
153 | #if (defined(CONFIG_BF537) || defined(CONFIG_BF534) || defined(CONFIG_BF536)) | ||
154 | case CH_EMAC_RX: | ||
155 | ret_irq = IRQ_MAC_RX; | ||
156 | break; | ||
157 | |||
158 | case CH_EMAC_TX: | ||
159 | ret_irq = IRQ_MAC_TX; | ||
160 | break; | ||
161 | |||
162 | case CH_UART1_RX: | ||
163 | ret_irq = IRQ_UART1_RX; | ||
164 | break; | ||
165 | |||
166 | case CH_UART1_TX: | ||
167 | ret_irq = IRQ_UART1_TX; | ||
168 | break; | ||
169 | #endif | ||
170 | |||
171 | case CH_SPORT0_RX: | ||
172 | ret_irq = IRQ_SPORT0_RX; | ||
173 | break; | ||
174 | |||
175 | case CH_SPORT0_TX: | ||
176 | ret_irq = IRQ_SPORT0_TX; | ||
177 | break; | ||
178 | |||
179 | case CH_SPORT1_RX: | ||
180 | ret_irq = IRQ_SPORT1_RX; | ||
181 | break; | ||
182 | |||
183 | case CH_SPORT1_TX: | ||
184 | ret_irq = IRQ_SPORT1_TX; | ||
185 | break; | ||
186 | |||
187 | case CH_SPI: | ||
188 | ret_irq = IRQ_SPI; | ||
189 | break; | ||
190 | |||
191 | case CH_UART_RX: | ||
192 | ret_irq = IRQ_UART_RX; | ||
193 | break; | ||
194 | |||
195 | case CH_UART_TX: | ||
196 | ret_irq = IRQ_UART_TX; | ||
197 | break; | ||
198 | |||
199 | case CH_MEM_STREAM0_SRC: | ||
200 | case CH_MEM_STREAM0_DEST: | ||
201 | ret_irq = IRQ_MEM_DMA0; | ||
202 | break; | ||
203 | |||
204 | case CH_MEM_STREAM1_SRC: | ||
205 | case CH_MEM_STREAM1_DEST: | ||
206 | ret_irq = IRQ_MEM_DMA1; | ||
207 | break; | ||
208 | } | ||
209 | return ret_irq; | ||
210 | } | ||
211 | |||
212 | # define channel2irq(channel) bf533_channel2irq(channel) | ||
213 | |||
214 | #else | ||
215 | |||
216 | static int bf561_channel2irq(unsigned int channel) | ||
217 | { | ||
218 | int ret_irq = -1; | ||
219 | |||
220 | switch (channel) { | ||
221 | case CH_PPI0: | ||
222 | ret_irq = IRQ_PPI0; | ||
223 | break; | ||
224 | case CH_PPI1: | ||
225 | ret_irq = IRQ_PPI1; | ||
226 | break; | ||
227 | case CH_SPORT0_RX: | ||
228 | ret_irq = IRQ_SPORT0_RX; | ||
229 | break; | ||
230 | case CH_SPORT0_TX: | ||
231 | ret_irq = IRQ_SPORT0_TX; | ||
232 | break; | ||
233 | case CH_SPORT1_RX: | ||
234 | ret_irq = IRQ_SPORT1_RX; | ||
235 | break; | ||
236 | case CH_SPORT1_TX: | ||
237 | ret_irq = IRQ_SPORT1_TX; | ||
238 | break; | ||
239 | case CH_SPI: | ||
240 | ret_irq = IRQ_SPI; | ||
241 | break; | ||
242 | case CH_UART_RX: | ||
243 | ret_irq = IRQ_UART_RX; | ||
244 | break; | ||
245 | case CH_UART_TX: | ||
246 | ret_irq = IRQ_UART_TX; | ||
247 | break; | ||
248 | |||
249 | case CH_MEM_STREAM0_SRC: | ||
250 | case CH_MEM_STREAM0_DEST: | ||
251 | ret_irq = IRQ_MEM_DMA0; | ||
252 | break; | ||
253 | case CH_MEM_STREAM1_SRC: | ||
254 | case CH_MEM_STREAM1_DEST: | ||
255 | ret_irq = IRQ_MEM_DMA1; | ||
256 | break; | ||
257 | case CH_MEM_STREAM2_SRC: | ||
258 | case CH_MEM_STREAM2_DEST: | ||
259 | ret_irq = IRQ_MEM_DMA2; | ||
260 | break; | ||
261 | case CH_MEM_STREAM3_SRC: | ||
262 | case CH_MEM_STREAM3_DEST: | ||
263 | ret_irq = IRQ_MEM_DMA3; | ||
264 | break; | ||
265 | |||
266 | case CH_IMEM_STREAM0_SRC: | ||
267 | case CH_IMEM_STREAM0_DEST: | ||
268 | ret_irq = IRQ_IMEM_DMA0; | ||
269 | break; | ||
270 | case CH_IMEM_STREAM1_SRC: | ||
271 | case CH_IMEM_STREAM1_DEST: | ||
272 | ret_irq = IRQ_IMEM_DMA1; | ||
273 | break; | ||
274 | } | ||
275 | return ret_irq; | ||
276 | } | ||
277 | |||
278 | # define channel2irq(channel) bf561_channel2irq(channel) | ||
279 | |||
280 | #endif | ||
281 | |||
282 | /*------------------------------------------------------------------------------ | ||
283 | * Request the specific DMA channel from the system. | ||
284 | *-----------------------------------------------------------------------------*/ | ||
285 | int request_dma(unsigned int channel, char *device_id) | ||
286 | { | ||
287 | |||
288 | pr_debug("request_dma() : BEGIN \n"); | ||
289 | mutex_lock(&(dma_ch[channel].dmalock)); | ||
290 | |||
291 | if ((dma_ch[channel].chan_status == DMA_CHANNEL_REQUESTED) | ||
292 | || (dma_ch[channel].chan_status == DMA_CHANNEL_ENABLED)) { | ||
293 | mutex_unlock(&(dma_ch[channel].dmalock)); | ||
294 | pr_debug("DMA CHANNEL IN USE \n"); | ||
295 | return -EBUSY; | ||
296 | } else { | ||
297 | dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED; | ||
298 | pr_debug("DMA CHANNEL IS ALLOCATED \n"); | ||
299 | } | ||
300 | |||
301 | mutex_unlock(&(dma_ch[channel].dmalock)); | ||
302 | |||
303 | dma_ch[channel].device_id = device_id; | ||
304 | dma_ch[channel].irq_callback = NULL; | ||
305 | |||
306 | /* This is to be enabled by putting a restriction - | ||
307 | * you have to request DMA, before doing any operations on | ||
308 | * descriptor/channel | ||
309 | */ | ||
310 | pr_debug("request_dma() : END \n"); | ||
311 | return channel; | ||
312 | } | ||
313 | EXPORT_SYMBOL(request_dma); | ||
314 | |||
315 | int set_dma_callback(unsigned int channel, dma_interrupt_t callback, void *data) | ||
316 | { | ||
317 | int ret_irq = 0; | ||
318 | |||
319 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
320 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
321 | |||
322 | if (callback != NULL) { | ||
323 | int ret_val; | ||
324 | ret_irq = channel2irq(channel); | ||
325 | |||
326 | dma_ch[channel].data = data; | ||
327 | |||
328 | ret_val = | ||
329 | request_irq(ret_irq, (void *)callback, IRQF_DISABLED, | ||
330 | dma_ch[channel].device_id, data); | ||
331 | if (ret_val) { | ||
332 | printk(KERN_NOTICE | ||
333 | "Request irq in DMA engine failed.\n"); | ||
334 | return -EPERM; | ||
335 | } | ||
336 | dma_ch[channel].irq_callback = callback; | ||
337 | } | ||
338 | return 0; | ||
339 | } | ||
340 | EXPORT_SYMBOL(set_dma_callback); | ||
341 | |||
342 | void free_dma(unsigned int channel) | ||
343 | { | ||
344 | int ret_irq; | ||
345 | |||
346 | pr_debug("freedma() : BEGIN \n"); | ||
347 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
348 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
349 | |||
350 | /* Halt the DMA */ | ||
351 | disable_dma(channel); | ||
352 | clear_dma_buffer(channel); | ||
353 | |||
354 | if (dma_ch[channel].irq_callback != NULL) { | ||
355 | ret_irq = channel2irq(channel); | ||
356 | free_irq(ret_irq, dma_ch[channel].data); | ||
357 | } | ||
358 | |||
359 | /* Clear the DMA Variable in the Channel */ | ||
360 | mutex_lock(&(dma_ch[channel].dmalock)); | ||
361 | dma_ch[channel].chan_status = DMA_CHANNEL_FREE; | ||
362 | mutex_unlock(&(dma_ch[channel].dmalock)); | ||
363 | |||
364 | pr_debug("freedma() : END \n"); | ||
365 | } | ||
366 | EXPORT_SYMBOL(free_dma); | ||
367 | |||
368 | void dma_enable_irq(unsigned int channel) | ||
369 | { | ||
370 | int ret_irq; | ||
371 | |||
372 | pr_debug("dma_enable_irq() : BEGIN \n"); | ||
373 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
374 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
375 | |||
376 | ret_irq = channel2irq(channel); | ||
377 | enable_irq(ret_irq); | ||
378 | } | ||
379 | EXPORT_SYMBOL(dma_enable_irq); | ||
380 | |||
381 | void dma_disable_irq(unsigned int channel) | ||
382 | { | ||
383 | int ret_irq; | ||
384 | |||
385 | pr_debug("dma_disable_irq() : BEGIN \n"); | ||
386 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
387 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
388 | |||
389 | ret_irq = channel2irq(channel); | ||
390 | disable_irq(ret_irq); | ||
391 | } | ||
392 | EXPORT_SYMBOL(dma_disable_irq); | ||
393 | |||
394 | int dma_channel_active(unsigned int channel) | ||
395 | { | ||
396 | if (dma_ch[channel].chan_status == DMA_CHANNEL_FREE) { | ||
397 | return 0; | ||
398 | } else { | ||
399 | return 1; | ||
400 | } | ||
401 | } | ||
402 | EXPORT_SYMBOL(dma_channel_active); | ||
403 | |||
404 | /*------------------------------------------------------------------------------ | ||
405 | * stop the specific DMA channel. | ||
406 | *-----------------------------------------------------------------------------*/ | ||
407 | void disable_dma(unsigned int channel) | ||
408 | { | ||
409 | pr_debug("stop_dma() : BEGIN \n"); | ||
410 | |||
411 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
412 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
413 | |||
414 | dma_ch[channel].regs->cfg &= ~DMAEN; /* Clean the enable bit */ | ||
415 | SSYNC(); | ||
416 | dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED; | ||
417 | /* Needs to be enabled Later */ | ||
418 | pr_debug("stop_dma() : END \n"); | ||
419 | return; | ||
420 | } | ||
421 | EXPORT_SYMBOL(disable_dma); | ||
422 | |||
423 | void enable_dma(unsigned int channel) | ||
424 | { | ||
425 | pr_debug("enable_dma() : BEGIN \n"); | ||
426 | |||
427 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
428 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
429 | |||
430 | dma_ch[channel].chan_status = DMA_CHANNEL_ENABLED; | ||
431 | dma_ch[channel].regs->curr_x_count = 0; | ||
432 | dma_ch[channel].regs->curr_y_count = 0; | ||
433 | |||
434 | dma_ch[channel].regs->cfg |= DMAEN; /* Set the enable bit */ | ||
435 | SSYNC(); | ||
436 | pr_debug("enable_dma() : END \n"); | ||
437 | return; | ||
438 | } | ||
439 | EXPORT_SYMBOL(enable_dma); | ||
440 | |||
441 | /*------------------------------------------------------------------------------ | ||
442 | * Set the Start Address register for the specific DMA channel | ||
443 | * This function can be used for register based DMA, | ||
444 | * to setup the start address | ||
445 | * addr: Starting address of the DMA Data to be transferred. | ||
446 | *-----------------------------------------------------------------------------*/ | ||
447 | void set_dma_start_addr(unsigned int channel, unsigned long addr) | ||
448 | { | ||
449 | pr_debug("set_dma_start_addr() : BEGIN \n"); | ||
450 | |||
451 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
452 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
453 | |||
454 | dma_ch[channel].regs->start_addr = addr; | ||
455 | SSYNC(); | ||
456 | pr_debug("set_dma_start_addr() : END\n"); | ||
457 | } | ||
458 | EXPORT_SYMBOL(set_dma_start_addr); | ||
459 | |||
460 | void set_dma_next_desc_addr(unsigned int channel, unsigned long addr) | ||
461 | { | ||
462 | pr_debug("set_dma_next_desc_addr() : BEGIN \n"); | ||
463 | |||
464 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
465 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
466 | |||
467 | dma_ch[channel].regs->next_desc_ptr = addr; | ||
468 | SSYNC(); | ||
469 | pr_debug("set_dma_start_addr() : END\n"); | ||
470 | } | ||
471 | EXPORT_SYMBOL(set_dma_next_desc_addr); | ||
472 | |||
473 | void set_dma_x_count(unsigned int channel, unsigned short x_count) | ||
474 | { | ||
475 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
476 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
477 | |||
478 | dma_ch[channel].regs->x_count = x_count; | ||
479 | SSYNC(); | ||
480 | } | ||
481 | EXPORT_SYMBOL(set_dma_x_count); | ||
482 | |||
483 | void set_dma_y_count(unsigned int channel, unsigned short y_count) | ||
484 | { | ||
485 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
486 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
487 | |||
488 | dma_ch[channel].regs->y_count = y_count; | ||
489 | SSYNC(); | ||
490 | } | ||
491 | EXPORT_SYMBOL(set_dma_y_count); | ||
492 | |||
493 | void set_dma_x_modify(unsigned int channel, short x_modify) | ||
494 | { | ||
495 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
496 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
497 | |||
498 | dma_ch[channel].regs->x_modify = x_modify; | ||
499 | SSYNC(); | ||
500 | } | ||
501 | EXPORT_SYMBOL(set_dma_x_modify); | ||
502 | |||
503 | void set_dma_y_modify(unsigned int channel, short y_modify) | ||
504 | { | ||
505 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
506 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
507 | |||
508 | dma_ch[channel].regs->y_modify = y_modify; | ||
509 | SSYNC(); | ||
510 | } | ||
511 | EXPORT_SYMBOL(set_dma_y_modify); | ||
512 | |||
513 | void set_dma_config(unsigned int channel, unsigned short config) | ||
514 | { | ||
515 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
516 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
517 | |||
518 | dma_ch[channel].regs->cfg = config; | ||
519 | SSYNC(); | ||
520 | } | ||
521 | EXPORT_SYMBOL(set_dma_config); | ||
522 | |||
523 | unsigned short | ||
524 | set_bfin_dma_config(char direction, char flow_mode, | ||
525 | char intr_mode, char dma_mode, char width) | ||
526 | { | ||
527 | unsigned short config; | ||
528 | |||
529 | config = | ||
530 | ((direction << 1) | (width << 2) | (dma_mode << 4) | | ||
531 | (intr_mode << 6) | (flow_mode << 12) | RESTART); | ||
532 | return config; | ||
533 | } | ||
534 | EXPORT_SYMBOL(set_bfin_dma_config); | ||
535 | |||
536 | void set_dma_sg(unsigned int channel, struct dmasg * sg, int nr_sg) | ||
537 | { | ||
538 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
539 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
540 | |||
541 | dma_ch[channel].regs->cfg |= ((nr_sg & 0x0F) << 8); | ||
542 | |||
543 | dma_ch[channel].regs->next_desc_ptr = (unsigned int)sg; | ||
544 | |||
545 | SSYNC(); | ||
546 | } | ||
547 | EXPORT_SYMBOL(set_dma_sg); | ||
548 | |||
549 | /*------------------------------------------------------------------------------ | ||
550 | * Get the DMA status of a specific DMA channel from the system. | ||
551 | *-----------------------------------------------------------------------------*/ | ||
552 | unsigned short get_dma_curr_irqstat(unsigned int channel) | ||
553 | { | ||
554 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
555 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
556 | |||
557 | return dma_ch[channel].regs->irq_status; | ||
558 | } | ||
559 | EXPORT_SYMBOL(get_dma_curr_irqstat); | ||
560 | |||
561 | /*------------------------------------------------------------------------------ | ||
562 | * Clear the DMA_DONE bit in DMA status. Stop the DMA completion interrupt. | ||
563 | *-----------------------------------------------------------------------------*/ | ||
564 | void clear_dma_irqstat(unsigned int channel) | ||
565 | { | ||
566 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
567 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
568 | dma_ch[channel].regs->irq_status |= 3; | ||
569 | } | ||
570 | EXPORT_SYMBOL(clear_dma_irqstat); | ||
571 | |||
572 | /*------------------------------------------------------------------------------ | ||
573 | * Get current DMA xcount of a specific DMA channel from the system. | ||
574 | *-----------------------------------------------------------------------------*/ | ||
575 | unsigned short get_dma_curr_xcount(unsigned int channel) | ||
576 | { | ||
577 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
578 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
579 | |||
580 | return dma_ch[channel].regs->curr_x_count; | ||
581 | } | ||
582 | EXPORT_SYMBOL(get_dma_curr_xcount); | ||
583 | |||
584 | /*------------------------------------------------------------------------------ | ||
585 | * Get current DMA ycount of a specific DMA channel from the system. | ||
586 | *-----------------------------------------------------------------------------*/ | ||
587 | unsigned short get_dma_curr_ycount(unsigned int channel) | ||
588 | { | ||
589 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | ||
590 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | ||
591 | |||
592 | return dma_ch[channel].regs->curr_y_count; | ||
593 | } | ||
594 | EXPORT_SYMBOL(get_dma_curr_ycount); | ||
595 | |||
596 | void *dma_memcpy(void *dest, const void *src, size_t size) | ||
597 | { | ||
598 | int direction; /* 1 - address decrease, 0 - address increase */ | ||
599 | int flag_align; /* 1 - address aligned, 0 - address unaligned */ | ||
600 | int flag_2D; /* 1 - 2D DMA needed, 0 - 1D DMA needed */ | ||
601 | |||
602 | if (size <= 0) | ||
603 | return NULL; | ||
604 | |||
605 | if ((unsigned long)src < memory_end) | ||
606 | blackfin_dcache_flush_range((unsigned int)src, | ||
607 | (unsigned int)(src + size)); | ||
608 | |||
609 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | ||
610 | |||
611 | if ((unsigned long)src < (unsigned long)dest) | ||
612 | direction = 1; | ||
613 | else | ||
614 | direction = 0; | ||
615 | |||
616 | if ((((unsigned long)dest % 2) == 0) && (((unsigned long)src % 2) == 0) | ||
617 | && ((size % 2) == 0)) | ||
618 | flag_align = 1; | ||
619 | else | ||
620 | flag_align = 0; | ||
621 | |||
622 | if (size > 0x10000) /* size > 64K */ | ||
623 | flag_2D = 1; | ||
624 | else | ||
625 | flag_2D = 0; | ||
626 | |||
627 | /* Setup destination and source start address */ | ||
628 | if (direction) { | ||
629 | if (flag_align) { | ||
630 | bfin_write_MDMA_D0_START_ADDR(dest + size - 2); | ||
631 | bfin_write_MDMA_S0_START_ADDR(src + size - 2); | ||
632 | } else { | ||
633 | bfin_write_MDMA_D0_START_ADDR(dest + size - 1); | ||
634 | bfin_write_MDMA_S0_START_ADDR(src + size - 1); | ||
635 | } | ||
636 | } else { | ||
637 | bfin_write_MDMA_D0_START_ADDR(dest); | ||
638 | bfin_write_MDMA_S0_START_ADDR(src); | ||
639 | } | ||
640 | |||
641 | /* Setup destination and source xcount */ | ||
642 | if (flag_2D) { | ||
643 | if (flag_align) { | ||
644 | bfin_write_MDMA_D0_X_COUNT(1024 / 2); | ||
645 | bfin_write_MDMA_S0_X_COUNT(1024 / 2); | ||
646 | } else { | ||
647 | bfin_write_MDMA_D0_X_COUNT(1024); | ||
648 | bfin_write_MDMA_S0_X_COUNT(1024); | ||
649 | } | ||
650 | bfin_write_MDMA_D0_Y_COUNT(size >> 10); | ||
651 | bfin_write_MDMA_S0_Y_COUNT(size >> 10); | ||
652 | } else { | ||
653 | if (flag_align) { | ||
654 | bfin_write_MDMA_D0_X_COUNT(size / 2); | ||
655 | bfin_write_MDMA_S0_X_COUNT(size / 2); | ||
656 | } else { | ||
657 | bfin_write_MDMA_D0_X_COUNT(size); | ||
658 | bfin_write_MDMA_S0_X_COUNT(size); | ||
659 | } | ||
660 | } | ||
661 | |||
662 | /* Setup destination and source xmodify and ymodify */ | ||
663 | if (direction) { | ||
664 | if (flag_align) { | ||
665 | bfin_write_MDMA_D0_X_MODIFY(-2); | ||
666 | bfin_write_MDMA_S0_X_MODIFY(-2); | ||
667 | if (flag_2D) { | ||
668 | bfin_write_MDMA_D0_Y_MODIFY(-2); | ||
669 | bfin_write_MDMA_S0_Y_MODIFY(-2); | ||
670 | } | ||
671 | } else { | ||
672 | bfin_write_MDMA_D0_X_MODIFY(-1); | ||
673 | bfin_write_MDMA_S0_X_MODIFY(-1); | ||
674 | if (flag_2D) { | ||
675 | bfin_write_MDMA_D0_Y_MODIFY(-1); | ||
676 | bfin_write_MDMA_S0_Y_MODIFY(-1); | ||
677 | } | ||
678 | } | ||
679 | } else { | ||
680 | if (flag_align) { | ||
681 | bfin_write_MDMA_D0_X_MODIFY(2); | ||
682 | bfin_write_MDMA_S0_X_MODIFY(2); | ||
683 | if (flag_2D) { | ||
684 | bfin_write_MDMA_D0_Y_MODIFY(2); | ||
685 | bfin_write_MDMA_S0_Y_MODIFY(2); | ||
686 | } | ||
687 | } else { | ||
688 | bfin_write_MDMA_D0_X_MODIFY(1); | ||
689 | bfin_write_MDMA_S0_X_MODIFY(1); | ||
690 | if (flag_2D) { | ||
691 | bfin_write_MDMA_D0_Y_MODIFY(1); | ||
692 | bfin_write_MDMA_S0_Y_MODIFY(1); | ||
693 | } | ||
694 | } | ||
695 | } | ||
696 | |||
697 | /* Enable source DMA */ | ||
698 | if (flag_2D) { | ||
699 | if (flag_align) { | ||
700 | bfin_write_MDMA_S0_CONFIG(DMAEN | DMA2D | WDSIZE_16); | ||
701 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | DMA2D | WDSIZE_16); | ||
702 | } else { | ||
703 | bfin_write_MDMA_S0_CONFIG(DMAEN | DMA2D); | ||
704 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | DMA2D); | ||
705 | } | ||
706 | } else { | ||
707 | if (flag_align) { | ||
708 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_16); | ||
709 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_16); | ||
710 | } else { | ||
711 | bfin_write_MDMA_S0_CONFIG(DMAEN); | ||
712 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN); | ||
713 | } | ||
714 | } | ||
715 | |||
716 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)) | ||
717 | ; | ||
718 | |||
719 | bfin_write_MDMA_D0_IRQ_STATUS(bfin_read_MDMA_D0_IRQ_STATUS() | | ||
720 | (DMA_DONE | DMA_ERR)); | ||
721 | |||
722 | bfin_write_MDMA_S0_CONFIG(0); | ||
723 | bfin_write_MDMA_D0_CONFIG(0); | ||
724 | |||
725 | if ((unsigned long)dest < memory_end) | ||
726 | blackfin_dcache_invalidate_range((unsigned int)dest, | ||
727 | (unsigned int)(dest + size)); | ||
728 | |||
729 | return dest; | ||
730 | } | ||
731 | EXPORT_SYMBOL(dma_memcpy); | ||
732 | |||
733 | void *safe_dma_memcpy(void *dest, const void *src, size_t size) | ||
734 | { | ||
735 | int flags = 0; | ||
736 | void *addr; | ||
737 | local_irq_save(flags); | ||
738 | addr = dma_memcpy(dest, src, size); | ||
739 | local_irq_restore(flags); | ||
740 | return addr; | ||
741 | } | ||
742 | EXPORT_SYMBOL(safe_dma_memcpy); | ||
diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c new file mode 100644 index 000000000000..e9f24a9a46ba --- /dev/null +++ b/arch/blackfin/kernel/bfin_gpio.c | |||
@@ -0,0 +1,637 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/bfin_gpio.c | ||
3 | * Based on: | ||
4 | * Author: Michael Hennerich (hennerich@blackfin.uclinux.org) | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: GPIO Abstraction Layer | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | /* | ||
31 | * Number BF537/6/4 BF561 BF533/2/1 | ||
32 | * | ||
33 | * GPIO_0 PF0 PF0 PF0 | ||
34 | * GPIO_1 PF1 PF1 PF1 | ||
35 | * GPIO_2 PF2 PF2 PF2 | ||
36 | * GPIO_3 PF3 PF3 PF3 | ||
37 | * GPIO_4 PF4 PF4 PF4 | ||
38 | * GPIO_5 PF5 PF5 PF5 | ||
39 | * GPIO_6 PF6 PF6 PF6 | ||
40 | * GPIO_7 PF7 PF7 PF7 | ||
41 | * GPIO_8 PF8 PF8 PF8 | ||
42 | * GPIO_9 PF9 PF9 PF9 | ||
43 | * GPIO_10 PF10 PF10 PF10 | ||
44 | * GPIO_11 PF11 PF11 PF11 | ||
45 | * GPIO_12 PF12 PF12 PF12 | ||
46 | * GPIO_13 PF13 PF13 PF13 | ||
47 | * GPIO_14 PF14 PF14 PF14 | ||
48 | * GPIO_15 PF15 PF15 PF15 | ||
49 | * GPIO_16 PG0 PF16 | ||
50 | * GPIO_17 PG1 PF17 | ||
51 | * GPIO_18 PG2 PF18 | ||
52 | * GPIO_19 PG3 PF19 | ||
53 | * GPIO_20 PG4 PF20 | ||
54 | * GPIO_21 PG5 PF21 | ||
55 | * GPIO_22 PG6 PF22 | ||
56 | * GPIO_23 PG7 PF23 | ||
57 | * GPIO_24 PG8 PF24 | ||
58 | * GPIO_25 PG9 PF25 | ||
59 | * GPIO_26 PG10 PF26 | ||
60 | * GPIO_27 PG11 PF27 | ||
61 | * GPIO_28 PG12 PF28 | ||
62 | * GPIO_29 PG13 PF29 | ||
63 | * GPIO_30 PG14 PF30 | ||
64 | * GPIO_31 PG15 PF31 | ||
65 | * GPIO_32 PH0 PF32 | ||
66 | * GPIO_33 PH1 PF33 | ||
67 | * GPIO_34 PH2 PF34 | ||
68 | * GPIO_35 PH3 PF35 | ||
69 | * GPIO_36 PH4 PF36 | ||
70 | * GPIO_37 PH5 PF37 | ||
71 | * GPIO_38 PH6 PF38 | ||
72 | * GPIO_39 PH7 PF39 | ||
73 | * GPIO_40 PH8 PF40 | ||
74 | * GPIO_41 PH9 PF41 | ||
75 | * GPIO_42 PH10 PF42 | ||
76 | * GPIO_43 PH11 PF43 | ||
77 | * GPIO_44 PH12 PF44 | ||
78 | * GPIO_45 PH13 PF45 | ||
79 | * GPIO_46 PH14 PF46 | ||
80 | * GPIO_47 PH15 PF47 | ||
81 | */ | ||
82 | |||
83 | #include <linux/module.h> | ||
84 | #include <linux/err.h> | ||
85 | #include <asm/blackfin.h> | ||
86 | #include <asm/gpio.h> | ||
87 | #include <linux/irq.h> | ||
88 | |||
89 | #ifdef BF533_FAMILY | ||
90 | static struct gpio_port_t *gpio_bankb[gpio_bank(MAX_BLACKFIN_GPIOS)] = { | ||
91 | (struct gpio_port_t *) FIO_FLAG_D, | ||
92 | }; | ||
93 | #endif | ||
94 | |||
95 | #ifdef BF537_FAMILY | ||
96 | static struct gpio_port_t *gpio_bankb[gpio_bank(MAX_BLACKFIN_GPIOS)] = { | ||
97 | (struct gpio_port_t *) PORTFIO, | ||
98 | (struct gpio_port_t *) PORTGIO, | ||
99 | (struct gpio_port_t *) PORTHIO, | ||
100 | }; | ||
101 | |||
102 | static unsigned short *port_fer[gpio_bank(MAX_BLACKFIN_GPIOS)] = { | ||
103 | (unsigned short *) PORTF_FER, | ||
104 | (unsigned short *) PORTG_FER, | ||
105 | (unsigned short *) PORTH_FER, | ||
106 | }; | ||
107 | |||
108 | #endif | ||
109 | |||
110 | #ifdef BF561_FAMILY | ||
111 | static struct gpio_port_t *gpio_bankb[gpio_bank(MAX_BLACKFIN_GPIOS)] = { | ||
112 | (struct gpio_port_t *) FIO0_FLAG_D, | ||
113 | (struct gpio_port_t *) FIO1_FLAG_D, | ||
114 | (struct gpio_port_t *) FIO2_FLAG_D, | ||
115 | }; | ||
116 | #endif | ||
117 | |||
118 | static unsigned short reserved_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; | ||
119 | |||
120 | #ifdef CONFIG_PM | ||
121 | static unsigned short wakeup_map[gpio_bank(MAX_BLACKFIN_GPIOS)]; | ||
122 | static unsigned char wakeup_flags_map[MAX_BLACKFIN_GPIOS]; | ||
123 | static struct gpio_port_s gpio_bank_saved[gpio_bank(MAX_BLACKFIN_GPIOS)]; | ||
124 | |||
125 | #ifdef BF533_FAMILY | ||
126 | static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG_INTB}; | ||
127 | #endif | ||
128 | |||
129 | #ifdef BF537_FAMILY | ||
130 | static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG_INTB, IRQ_PORTG_INTB, IRQ_MAC_TX}; | ||
131 | #endif | ||
132 | |||
133 | #ifdef BF561_FAMILY | ||
134 | static unsigned int sic_iwr_irqs[gpio_bank(MAX_BLACKFIN_GPIOS)] = {IRQ_PROG0_INTB, IRQ_PROG1_INTB, IRQ_PROG2_INTB}; | ||
135 | #endif | ||
136 | |||
137 | #endif /* CONFIG_PM */ | ||
138 | |||
139 | inline int check_gpio(unsigned short gpio) | ||
140 | { | ||
141 | if (gpio > MAX_BLACKFIN_GPIOS) | ||
142 | return -EINVAL; | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | #ifdef BF537_FAMILY | ||
147 | void port_setup(unsigned short gpio, unsigned short usage) | ||
148 | { | ||
149 | if (usage == GPIO_USAGE) { | ||
150 | if (*port_fer[gpio_bank(gpio)] & gpio_bit(gpio)) | ||
151 | printk(KERN_WARNING "bfin-gpio: Possible Conflict with Peripheral " | ||
152 | "usage and GPIO %d detected!\n", gpio); | ||
153 | *port_fer[gpio_bank(gpio)] &= ~gpio_bit(gpio); | ||
154 | } else | ||
155 | *port_fer[gpio_bank(gpio)] |= gpio_bit(gpio); | ||
156 | SSYNC(); | ||
157 | } | ||
158 | #else | ||
159 | # define port_setup(...) do { } while (0) | ||
160 | #endif | ||
161 | |||
162 | |||
163 | void default_gpio(unsigned short gpio) | ||
164 | { | ||
165 | unsigned short bank,bitmask; | ||
166 | |||
167 | bank = gpio_bank(gpio); | ||
168 | bitmask = gpio_bit(gpio); | ||
169 | |||
170 | gpio_bankb[bank]->maska_clear = bitmask; | ||
171 | gpio_bankb[bank]->maskb_clear = bitmask; | ||
172 | SSYNC(); | ||
173 | gpio_bankb[bank]->inen &= ~bitmask; | ||
174 | gpio_bankb[bank]->dir &= ~bitmask; | ||
175 | gpio_bankb[bank]->polar &= ~bitmask; | ||
176 | gpio_bankb[bank]->both &= ~bitmask; | ||
177 | gpio_bankb[bank]->edge &= ~bitmask; | ||
178 | } | ||
179 | |||
180 | |||
181 | int __init bfin_gpio_init(void) | ||
182 | { | ||
183 | int i; | ||
184 | |||
185 | printk(KERN_INFO "Blackfin GPIO Controller\n"); | ||
186 | |||
187 | for (i = 0; i < MAX_BLACKFIN_GPIOS; i+=GPIO_BANKSIZE) | ||
188 | reserved_map[gpio_bank(i)] = 0; | ||
189 | |||
190 | #if defined(BF537_FAMILY) && (defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE)) | ||
191 | # if defined(CONFIG_BFIN_MAC_RMII) | ||
192 | reserved_map[PORT_H] = 0xC373; | ||
193 | # else | ||
194 | reserved_map[PORT_H] = 0xFFFF; | ||
195 | # endif | ||
196 | #endif | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | arch_initcall(bfin_gpio_init); | ||
202 | |||
203 | |||
204 | /*********************************************************** | ||
205 | * | ||
206 | * FUNCTIONS: Blackfin General Purpose Ports Access Functions | ||
207 | * | ||
208 | * INPUTS/OUTPUTS: | ||
209 | * gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS | ||
210 | * | ||
211 | * | ||
212 | * DESCRIPTION: These functions abstract direct register access | ||
213 | * to Blackfin processor General Purpose | ||
214 | * Ports Regsiters | ||
215 | * | ||
216 | * CAUTION: These functions do not belong to the GPIO Driver API | ||
217 | ************************************************************* | ||
218 | * MODIFICATION HISTORY : | ||
219 | **************************************************************/ | ||
220 | |||
221 | /* Set a specific bit */ | ||
222 | |||
223 | #define SET_GPIO(name) \ | ||
224 | void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \ | ||
225 | { \ | ||
226 | unsigned long flags; \ | ||
227 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); \ | ||
228 | local_irq_save(flags); \ | ||
229 | if (arg) \ | ||
230 | gpio_bankb[gpio_bank(gpio)]->name |= gpio_bit(gpio); \ | ||
231 | else \ | ||
232 | gpio_bankb[gpio_bank(gpio)]->name &= ~gpio_bit(gpio); \ | ||
233 | local_irq_restore(flags); \ | ||
234 | } \ | ||
235 | EXPORT_SYMBOL(set_gpio_ ## name); | ||
236 | |||
237 | SET_GPIO(dir) | ||
238 | SET_GPIO(inen) | ||
239 | SET_GPIO(polar) | ||
240 | SET_GPIO(edge) | ||
241 | SET_GPIO(both) | ||
242 | |||
243 | |||
244 | #define SET_GPIO_SC(name) \ | ||
245 | void set_gpio_ ## name(unsigned short gpio, unsigned short arg) \ | ||
246 | { \ | ||
247 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); \ | ||
248 | if (arg) \ | ||
249 | gpio_bankb[gpio_bank(gpio)]->name ## _set = gpio_bit(gpio); \ | ||
250 | else \ | ||
251 | gpio_bankb[gpio_bank(gpio)]->name ## _clear = gpio_bit(gpio); \ | ||
252 | } \ | ||
253 | EXPORT_SYMBOL(set_gpio_ ## name); | ||
254 | |||
255 | SET_GPIO_SC(maska) | ||
256 | SET_GPIO_SC(maskb) | ||
257 | |||
258 | #if defined(ANOMALY_05000311) | ||
259 | void set_gpio_data(unsigned short gpio, unsigned short arg) | ||
260 | { | ||
261 | unsigned long flags; | ||
262 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
263 | local_irq_save(flags); | ||
264 | if (arg) | ||
265 | gpio_bankb[gpio_bank(gpio)]->data_set = gpio_bit(gpio); | ||
266 | else | ||
267 | gpio_bankb[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); | ||
268 | bfin_read_CHIPID(); | ||
269 | local_irq_restore(flags); | ||
270 | } | ||
271 | EXPORT_SYMBOL(set_gpio_data); | ||
272 | #else | ||
273 | SET_GPIO_SC(data) | ||
274 | #endif | ||
275 | |||
276 | |||
277 | #if defined(ANOMALY_05000311) | ||
278 | void set_gpio_toggle(unsigned short gpio) | ||
279 | { | ||
280 | unsigned long flags; | ||
281 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
282 | local_irq_save(flags); | ||
283 | gpio_bankb[gpio_bank(gpio)]->toggle = gpio_bit(gpio); | ||
284 | bfin_read_CHIPID(); | ||
285 | local_irq_restore(flags); | ||
286 | } | ||
287 | #else | ||
288 | void set_gpio_toggle(unsigned short gpio) | ||
289 | { | ||
290 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
291 | gpio_bankb[gpio_bank(gpio)]->toggle = gpio_bit(gpio); | ||
292 | } | ||
293 | #endif | ||
294 | EXPORT_SYMBOL(set_gpio_toggle); | ||
295 | |||
296 | |||
297 | /*Set current PORT date (16-bit word)*/ | ||
298 | |||
299 | #define SET_GPIO_P(name) \ | ||
300 | void set_gpiop_ ## name(unsigned short gpio, unsigned short arg) \ | ||
301 | { \ | ||
302 | gpio_bankb[gpio_bank(gpio)]->name = arg; \ | ||
303 | } \ | ||
304 | EXPORT_SYMBOL(set_gpiop_ ## name); | ||
305 | |||
306 | SET_GPIO_P(dir) | ||
307 | SET_GPIO_P(inen) | ||
308 | SET_GPIO_P(polar) | ||
309 | SET_GPIO_P(edge) | ||
310 | SET_GPIO_P(both) | ||
311 | SET_GPIO_P(maska) | ||
312 | SET_GPIO_P(maskb) | ||
313 | |||
314 | |||
315 | #if defined(ANOMALY_05000311) | ||
316 | void set_gpiop_data(unsigned short gpio, unsigned short arg) | ||
317 | { | ||
318 | unsigned long flags; | ||
319 | local_irq_save(flags); | ||
320 | gpio_bankb[gpio_bank(gpio)]->data = arg; | ||
321 | bfin_read_CHIPID(); | ||
322 | local_irq_restore(flags); | ||
323 | } | ||
324 | EXPORT_SYMBOL(set_gpiop_data); | ||
325 | #else | ||
326 | SET_GPIO_P(data) | ||
327 | #endif | ||
328 | |||
329 | |||
330 | |||
331 | /* Get a specific bit */ | ||
332 | |||
333 | #define GET_GPIO(name) \ | ||
334 | unsigned short get_gpio_ ## name(unsigned short gpio) \ | ||
335 | { \ | ||
336 | return (0x01 & (gpio_bankb[gpio_bank(gpio)]->name >> gpio_sub_n(gpio))); \ | ||
337 | } \ | ||
338 | EXPORT_SYMBOL(get_gpio_ ## name); | ||
339 | |||
340 | GET_GPIO(dir) | ||
341 | GET_GPIO(inen) | ||
342 | GET_GPIO(polar) | ||
343 | GET_GPIO(edge) | ||
344 | GET_GPIO(both) | ||
345 | GET_GPIO(maska) | ||
346 | GET_GPIO(maskb) | ||
347 | |||
348 | |||
349 | #if defined(ANOMALY_05000311) | ||
350 | unsigned short get_gpio_data(unsigned short gpio) | ||
351 | { | ||
352 | unsigned long flags; | ||
353 | unsigned short ret; | ||
354 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
355 | local_irq_save(flags); | ||
356 | ret = 0x01 & (gpio_bankb[gpio_bank(gpio)]->data >> gpio_sub_n(gpio)); | ||
357 | bfin_read_CHIPID(); | ||
358 | local_irq_restore(flags); | ||
359 | return ret; | ||
360 | } | ||
361 | EXPORT_SYMBOL(get_gpio_data); | ||
362 | #else | ||
363 | GET_GPIO(data) | ||
364 | #endif | ||
365 | |||
366 | /*Get current PORT date (16-bit word)*/ | ||
367 | |||
368 | #define GET_GPIO_P(name) \ | ||
369 | unsigned short get_gpiop_ ## name(unsigned short gpio) \ | ||
370 | { \ | ||
371 | return (gpio_bankb[gpio_bank(gpio)]->name);\ | ||
372 | } \ | ||
373 | EXPORT_SYMBOL(get_gpiop_ ## name); | ||
374 | |||
375 | GET_GPIO_P(dir) | ||
376 | GET_GPIO_P(inen) | ||
377 | GET_GPIO_P(polar) | ||
378 | GET_GPIO_P(edge) | ||
379 | GET_GPIO_P(both) | ||
380 | GET_GPIO_P(maska) | ||
381 | GET_GPIO_P(maskb) | ||
382 | |||
383 | #if defined(ANOMALY_05000311) | ||
384 | unsigned short get_gpiop_data(unsigned short gpio) | ||
385 | { | ||
386 | unsigned long flags; | ||
387 | unsigned short ret; | ||
388 | local_irq_save(flags); | ||
389 | ret = gpio_bankb[gpio_bank(gpio)]->data; | ||
390 | bfin_read_CHIPID(); | ||
391 | local_irq_restore(flags); | ||
392 | return ret; | ||
393 | } | ||
394 | EXPORT_SYMBOL(get_gpiop_data); | ||
395 | #else | ||
396 | GET_GPIO_P(data) | ||
397 | #endif | ||
398 | |||
399 | #ifdef CONFIG_PM | ||
400 | /*********************************************************** | ||
401 | * | ||
402 | * FUNCTIONS: Blackfin PM Setup API | ||
403 | * | ||
404 | * INPUTS/OUTPUTS: | ||
405 | * gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS | ||
406 | * type - | ||
407 | * PM_WAKE_RISING | ||
408 | * PM_WAKE_FALLING | ||
409 | * PM_WAKE_HIGH | ||
410 | * PM_WAKE_LOW | ||
411 | * PM_WAKE_BOTH_EDGES | ||
412 | * | ||
413 | * DESCRIPTION: Blackfin PM Driver API | ||
414 | * | ||
415 | * CAUTION: | ||
416 | ************************************************************* | ||
417 | * MODIFICATION HISTORY : | ||
418 | **************************************************************/ | ||
419 | int gpio_pm_wakeup_request(unsigned short gpio, unsigned char type) | ||
420 | { | ||
421 | unsigned long flags; | ||
422 | |||
423 | if ((check_gpio(gpio) < 0) || !type) | ||
424 | return -EINVAL; | ||
425 | |||
426 | local_irq_save(flags); | ||
427 | |||
428 | wakeup_map[gpio_bank(gpio)] |= gpio_bit(gpio); | ||
429 | wakeup_flags_map[gpio] = type; | ||
430 | local_irq_restore(flags); | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | EXPORT_SYMBOL(gpio_pm_wakeup_request); | ||
435 | |||
436 | void gpio_pm_wakeup_free(unsigned short gpio) | ||
437 | { | ||
438 | unsigned long flags; | ||
439 | |||
440 | if (check_gpio(gpio) < 0) | ||
441 | return; | ||
442 | |||
443 | local_irq_save(flags); | ||
444 | |||
445 | wakeup_map[gpio_bank(gpio)] &= ~gpio_bit(gpio); | ||
446 | |||
447 | local_irq_restore(flags); | ||
448 | } | ||
449 | EXPORT_SYMBOL(gpio_pm_wakeup_free); | ||
450 | |||
451 | static int bfin_gpio_wakeup_type(unsigned short gpio, unsigned char type) | ||
452 | { | ||
453 | port_setup(gpio, GPIO_USAGE); | ||
454 | set_gpio_dir(gpio, 0); | ||
455 | set_gpio_inen(gpio, 1); | ||
456 | |||
457 | if (type & (PM_WAKE_RISING | PM_WAKE_FALLING)) | ||
458 | set_gpio_edge(gpio, 1); | ||
459 | else | ||
460 | set_gpio_edge(gpio, 0); | ||
461 | |||
462 | if ((type & (PM_WAKE_BOTH_EDGES)) == (PM_WAKE_BOTH_EDGES)) | ||
463 | set_gpio_both(gpio, 1); | ||
464 | else | ||
465 | set_gpio_both(gpio, 0); | ||
466 | |||
467 | if ((type & (PM_WAKE_FALLING | PM_WAKE_LOW))) | ||
468 | set_gpio_polar(gpio, 1); | ||
469 | else | ||
470 | set_gpio_polar(gpio, 0); | ||
471 | |||
472 | SSYNC(); | ||
473 | |||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | u32 gpio_pm_setup(void) | ||
478 | { | ||
479 | u32 sic_iwr = 0; | ||
480 | u16 bank, mask, i, gpio; | ||
481 | |||
482 | for (i = 0; i < MAX_BLACKFIN_GPIOS; i+=GPIO_BANKSIZE) { | ||
483 | mask = wakeup_map[gpio_bank(i)]; | ||
484 | bank = gpio_bank(i); | ||
485 | |||
486 | gpio_bank_saved[bank].maskb = gpio_bankb[bank]->maskb; | ||
487 | gpio_bankb[bank]->maskb = 0; | ||
488 | |||
489 | if (mask) { | ||
490 | #ifdef BF537_FAMILY | ||
491 | gpio_bank_saved[bank].fer = *port_fer[bank]; | ||
492 | #endif | ||
493 | gpio_bank_saved[bank].inen = gpio_bankb[bank]->inen; | ||
494 | gpio_bank_saved[bank].polar = gpio_bankb[bank]->polar; | ||
495 | gpio_bank_saved[bank].dir = gpio_bankb[bank]->dir; | ||
496 | gpio_bank_saved[bank].edge = gpio_bankb[bank]->edge; | ||
497 | gpio_bank_saved[bank].both = gpio_bankb[bank]->both; | ||
498 | |||
499 | gpio = i; | ||
500 | |||
501 | while (mask) { | ||
502 | if (mask & 1) { | ||
503 | bfin_gpio_wakeup_type(gpio, wakeup_flags_map[gpio]); | ||
504 | set_gpio_data(gpio, 0); /*Clear*/ | ||
505 | } | ||
506 | gpio++; | ||
507 | mask >>= 1; | ||
508 | } | ||
509 | |||
510 | sic_iwr |= 1 << (sic_iwr_irqs[bank] - (IRQ_CORETMR + 1)); | ||
511 | gpio_bankb[bank]->maskb_set = wakeup_map[gpio_bank(i)]; | ||
512 | } | ||
513 | } | ||
514 | |||
515 | if (sic_iwr) | ||
516 | return sic_iwr; | ||
517 | else | ||
518 | return IWR_ENABLE_ALL; | ||
519 | } | ||
520 | |||
521 | |||
522 | void gpio_pm_restore(void) | ||
523 | { | ||
524 | u16 bank, mask, i; | ||
525 | |||
526 | for (i = 0; i < MAX_BLACKFIN_GPIOS; i+=GPIO_BANKSIZE) { | ||
527 | mask = wakeup_map[gpio_bank(i)]; | ||
528 | bank = gpio_bank(i); | ||
529 | |||
530 | if (mask) { | ||
531 | #ifdef BF537_FAMILY | ||
532 | *port_fer[bank] = gpio_bank_saved[bank].fer; | ||
533 | #endif | ||
534 | gpio_bankb[bank]->inen = gpio_bank_saved[bank].inen; | ||
535 | gpio_bankb[bank]->dir = gpio_bank_saved[bank].dir; | ||
536 | gpio_bankb[bank]->polar = gpio_bank_saved[bank].polar; | ||
537 | gpio_bankb[bank]->edge = gpio_bank_saved[bank].edge; | ||
538 | gpio_bankb[bank]->both = gpio_bank_saved[bank].both; | ||
539 | } | ||
540 | |||
541 | gpio_bankb[bank]->maskb = gpio_bank_saved[bank].maskb; | ||
542 | } | ||
543 | } | ||
544 | |||
545 | #endif | ||
546 | |||
547 | /*********************************************************** | ||
548 | * | ||
549 | * FUNCTIONS: Blackfin GPIO Driver | ||
550 | * | ||
551 | * INPUTS/OUTPUTS: | ||
552 | * gpio - GPIO Number between 0 and MAX_BLACKFIN_GPIOS | ||
553 | * | ||
554 | * | ||
555 | * DESCRIPTION: Blackfin GPIO Driver API | ||
556 | * | ||
557 | * CAUTION: | ||
558 | ************************************************************* | ||
559 | * MODIFICATION HISTORY : | ||
560 | **************************************************************/ | ||
561 | |||
562 | int gpio_request(unsigned short gpio, const char *label) | ||
563 | { | ||
564 | unsigned long flags; | ||
565 | |||
566 | if (check_gpio(gpio) < 0) | ||
567 | return -EINVAL; | ||
568 | |||
569 | local_irq_save(flags); | ||
570 | |||
571 | if (unlikely(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))) { | ||
572 | printk(KERN_ERR "bfin-gpio: GPIO %d is already reserved!\n", gpio); | ||
573 | dump_stack(); | ||
574 | local_irq_restore(flags); | ||
575 | return -EBUSY; | ||
576 | } | ||
577 | reserved_map[gpio_bank(gpio)] |= gpio_bit(gpio); | ||
578 | |||
579 | local_irq_restore(flags); | ||
580 | |||
581 | port_setup(gpio, GPIO_USAGE); | ||
582 | |||
583 | return 0; | ||
584 | } | ||
585 | EXPORT_SYMBOL(gpio_request); | ||
586 | |||
587 | |||
588 | void gpio_free(unsigned short gpio) | ||
589 | { | ||
590 | unsigned long flags; | ||
591 | |||
592 | if (check_gpio(gpio) < 0) | ||
593 | return; | ||
594 | |||
595 | local_irq_save(flags); | ||
596 | |||
597 | if (unlikely(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio)))) { | ||
598 | printk(KERN_ERR "bfin-gpio: GPIO %d wasn't reserved!\n", gpio); | ||
599 | dump_stack(); | ||
600 | local_irq_restore(flags); | ||
601 | return; | ||
602 | } | ||
603 | |||
604 | default_gpio(gpio); | ||
605 | |||
606 | reserved_map[gpio_bank(gpio)] &= ~gpio_bit(gpio); | ||
607 | |||
608 | local_irq_restore(flags); | ||
609 | } | ||
610 | EXPORT_SYMBOL(gpio_free); | ||
611 | |||
612 | |||
613 | void gpio_direction_input(unsigned short gpio) | ||
614 | { | ||
615 | unsigned long flags; | ||
616 | |||
617 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
618 | |||
619 | local_irq_save(flags); | ||
620 | gpio_bankb[gpio_bank(gpio)]->dir &= ~gpio_bit(gpio); | ||
621 | gpio_bankb[gpio_bank(gpio)]->inen |= gpio_bit(gpio); | ||
622 | local_irq_restore(flags); | ||
623 | } | ||
624 | EXPORT_SYMBOL(gpio_direction_input); | ||
625 | |||
626 | void gpio_direction_output(unsigned short gpio) | ||
627 | { | ||
628 | unsigned long flags; | ||
629 | |||
630 | BUG_ON(!(reserved_map[gpio_bank(gpio)] & gpio_bit(gpio))); | ||
631 | |||
632 | local_irq_save(flags); | ||
633 | gpio_bankb[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); | ||
634 | gpio_bankb[gpio_bank(gpio)]->dir |= gpio_bit(gpio); | ||
635 | local_irq_restore(flags); | ||
636 | } | ||
637 | EXPORT_SYMBOL(gpio_direction_output); | ||
diff --git a/arch/blackfin/kernel/bfin_ksyms.c b/arch/blackfin/kernel/bfin_ksyms.c new file mode 100644 index 000000000000..f64ecb638fab --- /dev/null +++ b/arch/blackfin/kernel/bfin_ksyms.c | |||
@@ -0,0 +1,119 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/bfin_ksyms.c | ||
3 | * Based on: none - original work | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <asm/irq.h> | ||
32 | #include <asm/checksum.h> | ||
33 | #include <asm/cacheflush.h> | ||
34 | #include <asm/uaccess.h> | ||
35 | |||
36 | /* platform dependent support */ | ||
37 | |||
38 | EXPORT_SYMBOL(__ioremap); | ||
39 | EXPORT_SYMBOL(strcmp); | ||
40 | EXPORT_SYMBOL(strncmp); | ||
41 | EXPORT_SYMBOL(dump_thread); | ||
42 | |||
43 | EXPORT_SYMBOL(ip_fast_csum); | ||
44 | |||
45 | EXPORT_SYMBOL(kernel_thread); | ||
46 | |||
47 | EXPORT_SYMBOL(__up); | ||
48 | EXPORT_SYMBOL(__down); | ||
49 | EXPORT_SYMBOL(__down_trylock); | ||
50 | EXPORT_SYMBOL(__down_interruptible); | ||
51 | |||
52 | EXPORT_SYMBOL(is_in_rom); | ||
53 | |||
54 | /* Networking helper routines. */ | ||
55 | EXPORT_SYMBOL(csum_partial_copy); | ||
56 | |||
57 | /* The following are special because they're not called | ||
58 | * explicitly (the C compiler generates them). Fortunately, | ||
59 | * their interface isn't gonna change any time soon now, so | ||
60 | * it's OK to leave it out of version control. | ||
61 | */ | ||
62 | EXPORT_SYMBOL(memcpy); | ||
63 | EXPORT_SYMBOL(memset); | ||
64 | EXPORT_SYMBOL(memcmp); | ||
65 | EXPORT_SYMBOL(memmove); | ||
66 | EXPORT_SYMBOL(memchr); | ||
67 | EXPORT_SYMBOL(get_wchan); | ||
68 | |||
69 | /* | ||
70 | * libgcc functions - functions that are used internally by the | ||
71 | * compiler... (prototypes are not correct though, but that | ||
72 | * doesn't really matter since they're not versioned). | ||
73 | */ | ||
74 | extern void __ashldi3(void); | ||
75 | extern void __ashrdi3(void); | ||
76 | extern void __smulsi3_highpart(void); | ||
77 | extern void __umulsi3_highpart(void); | ||
78 | extern void __divsi3(void); | ||
79 | extern void __lshrdi3(void); | ||
80 | extern void __modsi3(void); | ||
81 | extern void __muldi3(void); | ||
82 | extern void __udivsi3(void); | ||
83 | extern void __umodsi3(void); | ||
84 | |||
85 | /* gcc lib functions */ | ||
86 | EXPORT_SYMBOL(__ashldi3); | ||
87 | EXPORT_SYMBOL(__ashrdi3); | ||
88 | EXPORT_SYMBOL(__umulsi3_highpart); | ||
89 | EXPORT_SYMBOL(__smulsi3_highpart); | ||
90 | EXPORT_SYMBOL(__divsi3); | ||
91 | EXPORT_SYMBOL(__lshrdi3); | ||
92 | EXPORT_SYMBOL(__modsi3); | ||
93 | EXPORT_SYMBOL(__muldi3); | ||
94 | EXPORT_SYMBOL(__udivsi3); | ||
95 | EXPORT_SYMBOL(__umodsi3); | ||
96 | |||
97 | EXPORT_SYMBOL(outsb); | ||
98 | EXPORT_SYMBOL(insb); | ||
99 | EXPORT_SYMBOL(outsw); | ||
100 | EXPORT_SYMBOL(insw); | ||
101 | EXPORT_SYMBOL(outsl); | ||
102 | EXPORT_SYMBOL(insl); | ||
103 | EXPORT_SYMBOL(irq_flags); | ||
104 | EXPORT_SYMBOL(iounmap); | ||
105 | EXPORT_SYMBOL(blackfin_dcache_invalidate_range); | ||
106 | EXPORT_SYMBOL(blackfin_icache_dcache_flush_range); | ||
107 | EXPORT_SYMBOL(blackfin_icache_flush_range); | ||
108 | EXPORT_SYMBOL(blackfin_dcache_flush_range); | ||
109 | EXPORT_SYMBOL(blackfin_dflush_page); | ||
110 | |||
111 | EXPORT_SYMBOL(csum_partial); | ||
112 | EXPORT_SYMBOL(__init_begin); | ||
113 | EXPORT_SYMBOL(__init_end); | ||
114 | EXPORT_SYMBOL(_ebss_l1); | ||
115 | EXPORT_SYMBOL(_stext_l1); | ||
116 | EXPORT_SYMBOL(_etext_l1); | ||
117 | EXPORT_SYMBOL(_sdata_l1); | ||
118 | EXPORT_SYMBOL(_ebss_b_l1); | ||
119 | EXPORT_SYMBOL(_sdata_b_l1); | ||
diff --git a/arch/blackfin/kernel/dma-mapping.c b/arch/blackfin/kernel/dma-mapping.c new file mode 100644 index 000000000000..539eb24e062f --- /dev/null +++ b/arch/blackfin/kernel/dma-mapping.c | |||
@@ -0,0 +1,183 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/dma-mapping.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: Dynamic DMA mapping support. | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/types.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <linux/string.h> | ||
33 | #include <linux/bootmem.h> | ||
34 | #include <linux/spinlock.h> | ||
35 | #include <linux/device.h> | ||
36 | #include <linux/dma-mapping.h> | ||
37 | #include <asm/cacheflush.h> | ||
38 | #include <asm/io.h> | ||
39 | #include <asm/bfin-global.h> | ||
40 | |||
41 | static spinlock_t dma_page_lock; | ||
42 | static unsigned int *dma_page; | ||
43 | static unsigned int dma_pages; | ||
44 | static unsigned long dma_base; | ||
45 | static unsigned long dma_size; | ||
46 | static unsigned int dma_initialized; | ||
47 | |||
48 | void dma_alloc_init(unsigned long start, unsigned long end) | ||
49 | { | ||
50 | spin_lock_init(&dma_page_lock); | ||
51 | dma_initialized = 0; | ||
52 | |||
53 | dma_page = (unsigned int *)__get_free_page(GFP_KERNEL); | ||
54 | memset(dma_page, 0, PAGE_SIZE); | ||
55 | dma_base = PAGE_ALIGN(start); | ||
56 | dma_size = PAGE_ALIGN(end) - PAGE_ALIGN(start); | ||
57 | dma_pages = dma_size >> PAGE_SHIFT; | ||
58 | memset((void *)dma_base, 0, DMA_UNCACHED_REGION); | ||
59 | dma_initialized = 1; | ||
60 | |||
61 | printk(KERN_INFO "%s: dma_page @ 0x%p - %d pages at 0x%08lx\n", __FUNCTION__, | ||
62 | dma_page, dma_pages, dma_base); | ||
63 | } | ||
64 | |||
65 | static inline unsigned int get_pages(size_t size) | ||
66 | { | ||
67 | return ((size - 1) >> PAGE_SHIFT) + 1; | ||
68 | } | ||
69 | |||
70 | static unsigned long __alloc_dma_pages(unsigned int pages) | ||
71 | { | ||
72 | unsigned long ret = 0, flags; | ||
73 | int i, count = 0; | ||
74 | |||
75 | if (dma_initialized == 0) | ||
76 | dma_alloc_init(_ramend - DMA_UNCACHED_REGION, _ramend); | ||
77 | |||
78 | spin_lock_irqsave(&dma_page_lock, flags); | ||
79 | |||
80 | for (i = 0; i < dma_pages;) { | ||
81 | if (dma_page[i++] == 0) { | ||
82 | if (++count == pages) { | ||
83 | while (count--) | ||
84 | dma_page[--i] = 1; | ||
85 | ret = dma_base + (i << PAGE_SHIFT); | ||
86 | break; | ||
87 | } | ||
88 | } else | ||
89 | count = 0; | ||
90 | } | ||
91 | spin_unlock_irqrestore(&dma_page_lock, flags); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | static void __free_dma_pages(unsigned long addr, unsigned int pages) | ||
96 | { | ||
97 | unsigned long page = (addr - dma_base) >> PAGE_SHIFT; | ||
98 | unsigned long flags; | ||
99 | int i; | ||
100 | |||
101 | if ((page + pages) > dma_pages) { | ||
102 | printk(KERN_ERR "%s: freeing outside range.\n", __FUNCTION__); | ||
103 | BUG(); | ||
104 | } | ||
105 | |||
106 | spin_lock_irqsave(&dma_page_lock, flags); | ||
107 | for (i = page; i < page + pages; i++) { | ||
108 | dma_page[i] = 0; | ||
109 | } | ||
110 | spin_unlock_irqrestore(&dma_page_lock, flags); | ||
111 | } | ||
112 | |||
113 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
114 | dma_addr_t * dma_handle, gfp_t gfp) | ||
115 | { | ||
116 | void *ret; | ||
117 | |||
118 | ret = (void *)__alloc_dma_pages(get_pages(size)); | ||
119 | |||
120 | if (ret) { | ||
121 | memset(ret, 0, size); | ||
122 | *dma_handle = virt_to_phys(ret); | ||
123 | } | ||
124 | |||
125 | return ret; | ||
126 | } | ||
127 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
128 | |||
129 | void | ||
130 | dma_free_coherent(struct device *dev, size_t size, void *vaddr, | ||
131 | dma_addr_t dma_handle) | ||
132 | { | ||
133 | __free_dma_pages((unsigned long)vaddr, get_pages(size)); | ||
134 | } | ||
135 | EXPORT_SYMBOL(dma_free_coherent); | ||
136 | |||
137 | /* | ||
138 | * Dummy functions defined for some existing drivers | ||
139 | */ | ||
140 | |||
141 | dma_addr_t | ||
142 | dma_map_single(struct device *dev, void *ptr, size_t size, | ||
143 | enum dma_data_direction direction) | ||
144 | { | ||
145 | BUG_ON(direction == DMA_NONE); | ||
146 | |||
147 | invalidate_dcache_range((unsigned long)ptr, | ||
148 | (unsigned long)ptr + size); | ||
149 | |||
150 | return (dma_addr_t) ptr; | ||
151 | } | ||
152 | EXPORT_SYMBOL(dma_map_single); | ||
153 | |||
154 | int | ||
155 | dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
156 | enum dma_data_direction direction) | ||
157 | { | ||
158 | int i; | ||
159 | |||
160 | BUG_ON(direction == DMA_NONE); | ||
161 | |||
162 | for (i = 0; i < nents; i++) | ||
163 | invalidate_dcache_range(sg_dma_address(&sg[i]), | ||
164 | sg_dma_address(&sg[i]) + | ||
165 | sg_dma_len(&sg[i])); | ||
166 | |||
167 | return nents; | ||
168 | } | ||
169 | EXPORT_SYMBOL(dma_map_sg); | ||
170 | |||
171 | void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
172 | enum dma_data_direction direction) | ||
173 | { | ||
174 | BUG_ON(direction == DMA_NONE); | ||
175 | } | ||
176 | EXPORT_SYMBOL(dma_unmap_single); | ||
177 | |||
178 | void dma_unmap_sg(struct device *dev, struct scatterlist *sg, | ||
179 | int nhwentries, enum dma_data_direction direction) | ||
180 | { | ||
181 | BUG_ON(direction == DMA_NONE); | ||
182 | } | ||
183 | EXPORT_SYMBOL(dma_unmap_sg); | ||
diff --git a/arch/blackfin/kernel/dualcore_test.c b/arch/blackfin/kernel/dualcore_test.c new file mode 100644 index 000000000000..8b89c99f9dfa --- /dev/null +++ b/arch/blackfin/kernel/dualcore_test.c | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/dualcore_test.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: Small test code for CoreB on a BF561 | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/init.h> | ||
31 | #include <linux/module.h> | ||
32 | |||
33 | static int *testarg = (int*)0xfeb00000; | ||
34 | |||
35 | static int test_init(void) | ||
36 | { | ||
37 | *testarg = 1; | ||
38 | printk("Dual core test module inserted: set testarg = [%d]\n @ [%p]\n", | ||
39 | *testarg, testarg); | ||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static void test_exit(void) | ||
44 | { | ||
45 | printk("Dual core test module removed: testarg = [%d]\n", *testarg); | ||
46 | } | ||
47 | |||
48 | module_init(test_init); | ||
49 | module_exit(test_exit); | ||
diff --git a/arch/blackfin/kernel/entry.S b/arch/blackfin/kernel/entry.S new file mode 100644 index 000000000000..5880b270bd50 --- /dev/null +++ b/arch/blackfin/kernel/entry.S | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/entry.S | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/linkage.h> | ||
31 | #include <asm/thread_info.h> | ||
32 | #include <asm/errno.h> | ||
33 | #include <asm/asm-offsets.h> | ||
34 | |||
35 | #include <asm/mach-common/context.S> | ||
36 | |||
37 | #ifdef CONFIG_EXCPT_IRQ_SYSC_L1 | ||
38 | .section .l1.text | ||
39 | #else | ||
40 | .text | ||
41 | #endif | ||
42 | |||
43 | ENTRY(_ret_from_fork) | ||
44 | SP += -12; | ||
45 | call _schedule_tail; | ||
46 | SP += 12; | ||
47 | r0 = [sp + PT_IPEND]; | ||
48 | cc = bittst(r0,1); | ||
49 | if cc jump .Lin_kernel; | ||
50 | RESTORE_CONTEXT | ||
51 | rti; | ||
52 | .Lin_kernel: | ||
53 | bitclr(r0,1); | ||
54 | [sp + PT_IPEND] = r0; | ||
55 | /* do a 'fake' RTI by jumping to [RETI] | ||
56 | * to avoid clearing supervisor mode in child | ||
57 | */ | ||
58 | RESTORE_ALL_SYS | ||
59 | p0 = reti; | ||
60 | jump (p0); | ||
61 | |||
62 | ENTRY(_sys_fork) | ||
63 | r0 = -EINVAL; | ||
64 | rts; | ||
65 | |||
66 | ENTRY(_sys_vfork) | ||
67 | r0 = sp; | ||
68 | r0 += 24; | ||
69 | [--sp] = rets; | ||
70 | SP += -12; | ||
71 | call _bfin_vfork; | ||
72 | SP += 12; | ||
73 | rets = [sp++]; | ||
74 | rts; | ||
75 | |||
76 | ENTRY(_sys_clone) | ||
77 | r0 = sp; | ||
78 | r0 += 24; | ||
79 | [--sp] = rets; | ||
80 | SP += -12; | ||
81 | call _bfin_clone; | ||
82 | SP += 12; | ||
83 | rets = [sp++]; | ||
84 | rts; | ||
85 | |||
86 | ENTRY(_sys_rt_sigreturn) | ||
87 | r0 = sp; | ||
88 | r0 += 24; | ||
89 | [--sp] = rets; | ||
90 | SP += -12; | ||
91 | call _do_rt_sigreturn; | ||
92 | SP += 12; | ||
93 | rets = [sp++]; | ||
94 | rts; | ||
diff --git a/arch/blackfin/kernel/flat.c b/arch/blackfin/kernel/flat.c new file mode 100644 index 000000000000..a92587b628b5 --- /dev/null +++ b/arch/blackfin/kernel/flat.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * arch/blackfin/kernel/flat.c | ||
3 | * | ||
4 | * Copyright (C) 2007 Analog Devices, Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
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 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/flat.h> | ||
24 | |||
25 | #define FLAT_BFIN_RELOC_TYPE_16_BIT 0 | ||
26 | #define FLAT_BFIN_RELOC_TYPE_16H_BIT 1 | ||
27 | #define FLAT_BFIN_RELOC_TYPE_32_BIT 2 | ||
28 | |||
29 | unsigned long bfin_get_addr_from_rp(unsigned long *ptr, | ||
30 | unsigned long relval, | ||
31 | unsigned long flags, | ||
32 | unsigned long *persistent) | ||
33 | { | ||
34 | unsigned short *usptr = (unsigned short *)ptr; | ||
35 | int type = (relval >> 26) & 7; | ||
36 | unsigned long val; | ||
37 | |||
38 | switch (type) { | ||
39 | case FLAT_BFIN_RELOC_TYPE_16_BIT: | ||
40 | case FLAT_BFIN_RELOC_TYPE_16H_BIT: | ||
41 | usptr = (unsigned short *)ptr; | ||
42 | pr_debug("*usptr = %x", get_unaligned(usptr)); | ||
43 | val = get_unaligned(usptr); | ||
44 | val += *persistent; | ||
45 | break; | ||
46 | |||
47 | case FLAT_BFIN_RELOC_TYPE_32_BIT: | ||
48 | pr_debug("*ptr = %lx", get_unaligned(ptr)); | ||
49 | val = get_unaligned(ptr); | ||
50 | break; | ||
51 | |||
52 | default: | ||
53 | pr_debug("BINFMT_FLAT: Unknown relocation type %x\n", | ||
54 | type); | ||
55 | |||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Stack-relative relocs contain the offset into the stack, we | ||
61 | * have to add the stack's start address here and return 1 from | ||
62 | * flat_addr_absolute to prevent the normal address calculations | ||
63 | */ | ||
64 | if (relval & (1 << 29)) | ||
65 | return val + current->mm->context.end_brk; | ||
66 | |||
67 | if ((flags & FLAT_FLAG_GOTPIC) == 0) | ||
68 | val = htonl(val); | ||
69 | return val; | ||
70 | } | ||
71 | EXPORT_SYMBOL(bfin_get_addr_from_rp); | ||
72 | |||
73 | /* | ||
74 | * Insert the address ADDR into the symbol reference at RP; | ||
75 | * RELVAL is the raw relocation-table entry from which RP is derived | ||
76 | */ | ||
77 | void bfin_put_addr_at_rp(unsigned long *ptr, unsigned long addr, | ||
78 | unsigned long relval) | ||
79 | { | ||
80 | unsigned short *usptr = (unsigned short *)ptr; | ||
81 | int type = (relval >> 26) & 7; | ||
82 | |||
83 | switch (type) { | ||
84 | case FLAT_BFIN_RELOC_TYPE_16_BIT: | ||
85 | put_unaligned(addr, usptr); | ||
86 | pr_debug("new value %x at %p", get_unaligned(usptr), | ||
87 | usptr); | ||
88 | break; | ||
89 | |||
90 | case FLAT_BFIN_RELOC_TYPE_16H_BIT: | ||
91 | put_unaligned(addr >> 16, usptr); | ||
92 | pr_debug("new value %x", get_unaligned(usptr)); | ||
93 | break; | ||
94 | |||
95 | case FLAT_BFIN_RELOC_TYPE_32_BIT: | ||
96 | put_unaligned(addr, ptr); | ||
97 | pr_debug("new ptr =%lx", get_unaligned(ptr)); | ||
98 | break; | ||
99 | } | ||
100 | } | ||
101 | EXPORT_SYMBOL(bfin_put_addr_at_rp); | ||
diff --git a/arch/blackfin/kernel/init_task.c b/arch/blackfin/kernel/init_task.c new file mode 100644 index 000000000000..b45188f8512e --- /dev/null +++ b/arch/blackfin/kernel/init_task.c | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/init_task.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: This file contains the simple DMA Implementation for Blackfin | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/mm.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/init_task.h> | ||
33 | #include <linux/mqueue.h> | ||
34 | |||
35 | static struct fs_struct init_fs = INIT_FS; | ||
36 | static struct files_struct init_files = INIT_FILES; | ||
37 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
38 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
39 | |||
40 | struct mm_struct init_mm = INIT_MM(init_mm); | ||
41 | EXPORT_SYMBOL(init_mm); | ||
42 | |||
43 | /* | ||
44 | * Initial task structure. | ||
45 | * | ||
46 | * All other task structs will be allocated on slabs in fork.c | ||
47 | */ | ||
48 | struct task_struct init_task = INIT_TASK(init_task); | ||
49 | EXPORT_SYMBOL(init_task); | ||
50 | |||
51 | /* | ||
52 | * Initial thread structure. | ||
53 | * | ||
54 | * We need to make sure that this is 8192-byte aligned due to the | ||
55 | * way process stacks are handled. This is done by having a special | ||
56 | * "init_task" linker map entry. | ||
57 | */ | ||
58 | union thread_union init_thread_union | ||
59 | __attribute__ ((__section__(".data.init_task"))) = { | ||
60 | INIT_THREAD_INFO(init_task)}; | ||
diff --git a/arch/blackfin/kernel/irqchip.c b/arch/blackfin/kernel/irqchip.c new file mode 100644 index 000000000000..df5bf022cf79 --- /dev/null +++ b/arch/blackfin/kernel/irqchip.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/irqchip.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: This file contains the simple DMA Implementation for Blackfin | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/kernel_stat.h> | ||
31 | #include <linux/module.h> | ||
32 | #include <linux/random.h> | ||
33 | #include <linux/seq_file.h> | ||
34 | #include <linux/kallsyms.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/irq.h> | ||
37 | |||
38 | static unsigned long irq_err_count; | ||
39 | static spinlock_t irq_controller_lock; | ||
40 | |||
41 | /* | ||
42 | * Dummy mask/unmask handler | ||
43 | */ | ||
44 | void dummy_mask_unmask_irq(unsigned int irq) | ||
45 | { | ||
46 | } | ||
47 | |||
48 | void ack_bad_irq(unsigned int irq) | ||
49 | { | ||
50 | irq_err_count += 1; | ||
51 | printk(KERN_ERR "IRQ: spurious interrupt %d\n", irq); | ||
52 | } | ||
53 | EXPORT_SYMBOL(ack_bad_irq); | ||
54 | |||
55 | static struct irq_chip bad_chip = { | ||
56 | .ack = dummy_mask_unmask_irq, | ||
57 | .mask = dummy_mask_unmask_irq, | ||
58 | .unmask = dummy_mask_unmask_irq, | ||
59 | }; | ||
60 | |||
61 | static struct irq_desc bad_irq_desc = { | ||
62 | .chip = &bad_chip, | ||
63 | .handle_irq = handle_bad_irq, | ||
64 | .depth = 1, | ||
65 | }; | ||
66 | |||
67 | int show_interrupts(struct seq_file *p, void *v) | ||
68 | { | ||
69 | int i = *(loff_t *) v; | ||
70 | struct irqaction *action; | ||
71 | unsigned long flags; | ||
72 | |||
73 | if (i < NR_IRQS) { | ||
74 | spin_lock_irqsave(&irq_desc[i].lock, flags); | ||
75 | action = irq_desc[i].action; | ||
76 | if (!action) | ||
77 | goto unlock; | ||
78 | |||
79 | seq_printf(p, "%3d: %10u ", i, kstat_irqs(i)); | ||
80 | seq_printf(p, " %s", action->name); | ||
81 | for (action = action->next; action; action = action->next) | ||
82 | seq_printf(p, ", %s", action->name); | ||
83 | |||
84 | seq_putc(p, '\n'); | ||
85 | unlock: | ||
86 | spin_unlock_irqrestore(&irq_desc[i].lock, flags); | ||
87 | } else if (i == NR_IRQS) { | ||
88 | seq_printf(p, "Err: %10lu\n", irq_err_count); | ||
89 | } | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * do_IRQ handles all hardware IRQ's. Decoded IRQs should not | ||
95 | * come via this function. Instead, they should provide their | ||
96 | * own 'handler' | ||
97 | */ | ||
98 | |||
99 | #ifdef CONFIG_DO_IRQ_L1 | ||
100 | asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)__attribute__((l1_text)); | ||
101 | #endif | ||
102 | |||
103 | asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) | ||
104 | { | ||
105 | struct pt_regs *old_regs; | ||
106 | struct irq_desc *desc = irq_desc + irq; | ||
107 | unsigned short pending, other_ints; | ||
108 | |||
109 | old_regs = set_irq_regs(regs); | ||
110 | |||
111 | /* | ||
112 | * Some hardware gives randomly wrong interrupts. Rather | ||
113 | * than crashing, do something sensible. | ||
114 | */ | ||
115 | if (irq >= NR_IRQS) | ||
116 | desc = &bad_irq_desc; | ||
117 | |||
118 | irq_enter(); | ||
119 | |||
120 | generic_handle_irq(irq); | ||
121 | |||
122 | /* If we're the only interrupt running (ignoring IRQ15 which is for | ||
123 | syscalls), lower our priority to IRQ14 so that softirqs run at | ||
124 | that level. If there's another, lower-level interrupt, irq_exit | ||
125 | will defer softirqs to that. */ | ||
126 | CSYNC(); | ||
127 | pending = bfin_read_IPEND() & ~0x8000; | ||
128 | other_ints = pending & (pending - 1); | ||
129 | if (other_ints == 0) | ||
130 | lower_to_irq14(); | ||
131 | irq_exit(); | ||
132 | |||
133 | set_irq_regs(old_regs); | ||
134 | } | ||
135 | |||
136 | void __init init_IRQ(void) | ||
137 | { | ||
138 | struct irq_desc *desc; | ||
139 | int irq; | ||
140 | |||
141 | spin_lock_init(&irq_controller_lock); | ||
142 | for (irq = 0, desc = irq_desc; irq < NR_IRQS; irq++, desc++) { | ||
143 | *desc = bad_irq_desc; | ||
144 | } | ||
145 | |||
146 | init_arch_irq(); | ||
147 | } | ||
diff --git a/arch/blackfin/kernel/module.c b/arch/blackfin/kernel/module.c new file mode 100644 index 000000000000..372f756f1ad9 --- /dev/null +++ b/arch/blackfin/kernel/module.c | |||
@@ -0,0 +1,429 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/module.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | |||
31 | #include <linux/moduleloader.h> | ||
32 | #include <linux/elf.h> | ||
33 | #include <linux/vmalloc.h> | ||
34 | #include <linux/fs.h> | ||
35 | #include <linux/string.h> | ||
36 | #include <linux/kernel.h> | ||
37 | #include <asm/dma.h> | ||
38 | #include <asm/cacheflush.h> | ||
39 | |||
40 | /* | ||
41 | * handle arithmetic relocations. | ||
42 | * See binutils/bfd/elf32-bfin.c for more details | ||
43 | */ | ||
44 | #define RELOC_STACK_SIZE 100 | ||
45 | static uint32_t reloc_stack[RELOC_STACK_SIZE]; | ||
46 | static unsigned int reloc_stack_tos; | ||
47 | |||
48 | #define is_reloc_stack_empty() ((reloc_stack_tos > 0)?0:1) | ||
49 | |||
50 | static void reloc_stack_push(uint32_t value) | ||
51 | { | ||
52 | reloc_stack[reloc_stack_tos++] = value; | ||
53 | } | ||
54 | |||
55 | static uint32_t reloc_stack_pop(void) | ||
56 | { | ||
57 | return reloc_stack[--reloc_stack_tos]; | ||
58 | } | ||
59 | |||
60 | static uint32_t reloc_stack_operate(unsigned int oper, struct module *mod) | ||
61 | { | ||
62 | uint32_t value; | ||
63 | |||
64 | switch (oper) { | ||
65 | case R_add: | ||
66 | value = reloc_stack[reloc_stack_tos - 2] + | ||
67 | reloc_stack[reloc_stack_tos - 1]; | ||
68 | reloc_stack_tos -= 2; | ||
69 | break; | ||
70 | case R_sub: | ||
71 | value = reloc_stack[reloc_stack_tos - 2] - | ||
72 | reloc_stack[reloc_stack_tos - 1]; | ||
73 | reloc_stack_tos -= 2; | ||
74 | break; | ||
75 | case R_mult: | ||
76 | value = reloc_stack[reloc_stack_tos - 2] * | ||
77 | reloc_stack[reloc_stack_tos - 1]; | ||
78 | reloc_stack_tos -= 2; | ||
79 | break; | ||
80 | case R_div: | ||
81 | value = reloc_stack[reloc_stack_tos - 2] / | ||
82 | reloc_stack[reloc_stack_tos - 1]; | ||
83 | reloc_stack_tos -= 2; | ||
84 | break; | ||
85 | case R_mod: | ||
86 | value = reloc_stack[reloc_stack_tos - 2] % | ||
87 | reloc_stack[reloc_stack_tos - 1]; | ||
88 | reloc_stack_tos -= 2; | ||
89 | break; | ||
90 | case R_lshift: | ||
91 | value = reloc_stack[reloc_stack_tos - 2] << | ||
92 | reloc_stack[reloc_stack_tos - 1]; | ||
93 | reloc_stack_tos -= 2; | ||
94 | break; | ||
95 | case R_rshift: | ||
96 | value = reloc_stack[reloc_stack_tos - 2] >> | ||
97 | reloc_stack[reloc_stack_tos - 1]; | ||
98 | reloc_stack_tos -= 2; | ||
99 | break; | ||
100 | case R_and: | ||
101 | value = reloc_stack[reloc_stack_tos - 2] & | ||
102 | reloc_stack[reloc_stack_tos - 1]; | ||
103 | reloc_stack_tos -= 2; | ||
104 | break; | ||
105 | case R_or: | ||
106 | value = reloc_stack[reloc_stack_tos - 2] | | ||
107 | reloc_stack[reloc_stack_tos - 1]; | ||
108 | reloc_stack_tos -= 2; | ||
109 | break; | ||
110 | case R_xor: | ||
111 | value = reloc_stack[reloc_stack_tos - 2] ^ | ||
112 | reloc_stack[reloc_stack_tos - 1]; | ||
113 | reloc_stack_tos -= 2; | ||
114 | break; | ||
115 | case R_land: | ||
116 | value = reloc_stack[reloc_stack_tos - 2] && | ||
117 | reloc_stack[reloc_stack_tos - 1]; | ||
118 | reloc_stack_tos -= 2; | ||
119 | break; | ||
120 | case R_lor: | ||
121 | value = reloc_stack[reloc_stack_tos - 2] || | ||
122 | reloc_stack[reloc_stack_tos - 1]; | ||
123 | reloc_stack_tos -= 2; | ||
124 | break; | ||
125 | case R_neg: | ||
126 | value = -reloc_stack[reloc_stack_tos - 1]; | ||
127 | reloc_stack_tos--; | ||
128 | break; | ||
129 | case R_comp: | ||
130 | value = ~reloc_stack[reloc_stack_tos - 1]; | ||
131 | reloc_stack_tos -= 1; | ||
132 | break; | ||
133 | default: | ||
134 | printk(KERN_WARNING "module %s: unhandled reloction\n", | ||
135 | mod->name); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /* now push the new value back on stack */ | ||
140 | reloc_stack_push(value); | ||
141 | |||
142 | return value; | ||
143 | } | ||
144 | |||
145 | void *module_alloc(unsigned long size) | ||
146 | { | ||
147 | if (size == 0) | ||
148 | return NULL; | ||
149 | return vmalloc(size); | ||
150 | } | ||
151 | |||
152 | /* Free memory returned from module_alloc */ | ||
153 | void module_free(struct module *mod, void *module_region) | ||
154 | { | ||
155 | vfree(module_region); | ||
156 | } | ||
157 | |||
158 | /* Transfer the section to the L1 memory */ | ||
159 | int | ||
160 | module_frob_arch_sections(Elf_Ehdr * hdr, Elf_Shdr * sechdrs, | ||
161 | char *secstrings, struct module *mod) | ||
162 | { | ||
163 | Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum; | ||
164 | void *dest = NULL; | ||
165 | |||
166 | for (s = sechdrs; s < sechdrs_end; ++s) { | ||
167 | if ((strcmp(".l1.text", secstrings + s->sh_name) == 0) || | ||
168 | ((strcmp(".text", secstrings + s->sh_name)==0) && | ||
169 | (hdr->e_flags & FLG_CODE_IN_L1) && (s->sh_size > 0))) { | ||
170 | mod->arch.text_l1 = s; | ||
171 | dest = l1_inst_sram_alloc(s->sh_size); | ||
172 | if (dest == NULL) { | ||
173 | printk(KERN_ERR | ||
174 | "module %s: L1 instruction memory allocation failed\n", | ||
175 | mod->name); | ||
176 | return -1; | ||
177 | } | ||
178 | dma_memcpy(dest, (void *)s->sh_addr, s->sh_size); | ||
179 | s->sh_flags &= ~SHF_ALLOC; | ||
180 | s->sh_addr = (unsigned long)dest; | ||
181 | } | ||
182 | if ((strcmp(".l1.data", secstrings + s->sh_name) == 0)|| | ||
183 | ((strcmp(".data", secstrings + s->sh_name)==0) && | ||
184 | (hdr->e_flags & FLG_DATA_IN_L1) && (s->sh_size > 0))) { | ||
185 | mod->arch.data_a_l1 = s; | ||
186 | dest = l1_data_sram_alloc(s->sh_size); | ||
187 | if (dest == NULL) { | ||
188 | printk(KERN_ERR | ||
189 | "module %s: L1 data memory allocation failed\n", | ||
190 | mod->name); | ||
191 | return -1; | ||
192 | } | ||
193 | memcpy(dest, (void *)s->sh_addr, s->sh_size); | ||
194 | s->sh_flags &= ~SHF_ALLOC; | ||
195 | s->sh_addr = (unsigned long)dest; | ||
196 | } | ||
197 | if (strcmp(".l1.bss", secstrings + s->sh_name) == 0 || | ||
198 | ((strcmp(".bss", secstrings + s->sh_name)==0) && | ||
199 | (hdr->e_flags & FLG_DATA_IN_L1) && (s->sh_size > 0))) { | ||
200 | mod->arch.bss_a_l1 = s; | ||
201 | dest = l1_data_sram_alloc(s->sh_size); | ||
202 | if (dest == NULL) { | ||
203 | printk(KERN_ERR | ||
204 | "module %s: L1 data memory allocation failed\n", | ||
205 | mod->name); | ||
206 | return -1; | ||
207 | } | ||
208 | memset(dest, 0, s->sh_size); | ||
209 | s->sh_flags &= ~SHF_ALLOC; | ||
210 | s->sh_addr = (unsigned long)dest; | ||
211 | } | ||
212 | if (strcmp(".l1.data.B", secstrings + s->sh_name) == 0) { | ||
213 | mod->arch.data_b_l1 = s; | ||
214 | dest = l1_data_B_sram_alloc(s->sh_size); | ||
215 | if (dest == NULL) { | ||
216 | printk(KERN_ERR | ||
217 | "module %s: L1 data memory allocation failed\n", | ||
218 | mod->name); | ||
219 | return -1; | ||
220 | } | ||
221 | memcpy(dest, (void *)s->sh_addr, s->sh_size); | ||
222 | s->sh_flags &= ~SHF_ALLOC; | ||
223 | s->sh_addr = (unsigned long)dest; | ||
224 | } | ||
225 | if (strcmp(".l1.bss.B", secstrings + s->sh_name) == 0) { | ||
226 | mod->arch.bss_b_l1 = s; | ||
227 | dest = l1_data_B_sram_alloc(s->sh_size); | ||
228 | if (dest == NULL) { | ||
229 | printk(KERN_ERR | ||
230 | "module %s: L1 data memory allocation failed\n", | ||
231 | mod->name); | ||
232 | return -1; | ||
233 | } | ||
234 | memset(dest, 0, s->sh_size); | ||
235 | s->sh_flags &= ~SHF_ALLOC; | ||
236 | s->sh_addr = (unsigned long)dest; | ||
237 | } | ||
238 | } | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | int | ||
243 | apply_relocate(Elf_Shdr * sechdrs, const char *strtab, | ||
244 | unsigned int symindex, unsigned int relsec, struct module *me) | ||
245 | { | ||
246 | printk(KERN_ERR "module %s: .rel unsupported\n", me->name); | ||
247 | return -ENOEXEC; | ||
248 | } | ||
249 | |||
250 | /*************************************************************************/ | ||
251 | /* FUNCTION : apply_relocate_add */ | ||
252 | /* ABSTRACT : Blackfin specific relocation handling for the loadable */ | ||
253 | /* modules. Modules are expected to be .o files. */ | ||
254 | /* Arithmetic relocations are handled. */ | ||
255 | /* We do not expect LSETUP to be split and hence is not */ | ||
256 | /* handled. */ | ||
257 | /* R_byte and R_byte2 are also not handled as the gas */ | ||
258 | /* does not generate it. */ | ||
259 | /*************************************************************************/ | ||
260 | int | ||
261 | apply_relocate_add(Elf_Shdr * sechdrs, const char *strtab, | ||
262 | unsigned int symindex, unsigned int relsec, | ||
263 | struct module *mod) | ||
264 | { | ||
265 | unsigned int i; | ||
266 | unsigned short tmp; | ||
267 | Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; | ||
268 | Elf32_Sym *sym; | ||
269 | uint32_t *location32; | ||
270 | uint16_t *location16; | ||
271 | uint32_t value; | ||
272 | |||
273 | pr_debug("Applying relocate section %u to %u\n", relsec, | ||
274 | sechdrs[relsec].sh_info); | ||
275 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
276 | /* This is where to make the change */ | ||
277 | location16 = | ||
278 | (uint16_t *) (sechdrs[sechdrs[relsec].sh_info].sh_addr + | ||
279 | rel[i].r_offset); | ||
280 | location32 = (uint32_t *) location16; | ||
281 | /* This is the symbol it is referring to. Note that all | ||
282 | undefined symbols have been resolved. */ | ||
283 | sym = (Elf32_Sym *) sechdrs[symindex].sh_addr | ||
284 | + ELF32_R_SYM(rel[i].r_info); | ||
285 | if (is_reloc_stack_empty()) { | ||
286 | value = sym->st_value; | ||
287 | } else { | ||
288 | value = reloc_stack_pop(); | ||
289 | } | ||
290 | value += rel[i].r_addend; | ||
291 | pr_debug("location is %x, value is %x type is %d \n", | ||
292 | (unsigned int) location32, value, | ||
293 | ELF32_R_TYPE(rel[i].r_info)); | ||
294 | |||
295 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
296 | |||
297 | case R_pcrel24: | ||
298 | case R_pcrel24_jump_l: | ||
299 | /* Add the value, subtract its postition */ | ||
300 | location16 = | ||
301 | (uint16_t *) (sechdrs[sechdrs[relsec].sh_info]. | ||
302 | sh_addr + rel[i].r_offset - 2); | ||
303 | location32 = (uint32_t *) location16; | ||
304 | value -= (uint32_t) location32; | ||
305 | value >>= 1; | ||
306 | pr_debug("value is %x, before %x-%x after %x-%x\n", value, | ||
307 | *location16, *(location16 + 1), | ||
308 | (*location16 & 0xff00) | (value >> 16 & 0x00ff), | ||
309 | value & 0xffff); | ||
310 | *location16 = | ||
311 | (*location16 & 0xff00) | (value >> 16 & 0x00ff); | ||
312 | *(location16 + 1) = value & 0xffff; | ||
313 | break; | ||
314 | case R_pcrel12_jump: | ||
315 | case R_pcrel12_jump_s: | ||
316 | value -= (uint32_t) location32; | ||
317 | value >>= 1; | ||
318 | *location16 = (value & 0xfff); | ||
319 | break; | ||
320 | case R_pcrel10: | ||
321 | value -= (uint32_t) location32; | ||
322 | value >>= 1; | ||
323 | *location16 = (value & 0x3ff); | ||
324 | break; | ||
325 | case R_luimm16: | ||
326 | pr_debug("before %x after %x\n", *location16, | ||
327 | (value & 0xffff)); | ||
328 | tmp = (value & 0xffff); | ||
329 | if((unsigned long)location16 >= L1_CODE_START) { | ||
330 | dma_memcpy(location16, &tmp, 2); | ||
331 | } else | ||
332 | *location16 = tmp; | ||
333 | break; | ||
334 | case R_huimm16: | ||
335 | pr_debug("before %x after %x\n", *location16, | ||
336 | ((value >> 16) & 0xffff)); | ||
337 | tmp = ((value >> 16) & 0xffff); | ||
338 | if((unsigned long)location16 >= L1_CODE_START) { | ||
339 | dma_memcpy(location16, &tmp, 2); | ||
340 | } else | ||
341 | *location16 = tmp; | ||
342 | break; | ||
343 | case R_rimm16: | ||
344 | *location16 = (value & 0xffff); | ||
345 | break; | ||
346 | case R_byte4_data: | ||
347 | pr_debug("before %x after %x\n", *location32, value); | ||
348 | *location32 = value; | ||
349 | break; | ||
350 | case R_push: | ||
351 | reloc_stack_push(value); | ||
352 | break; | ||
353 | case R_const: | ||
354 | reloc_stack_push(rel[i].r_addend); | ||
355 | break; | ||
356 | case R_add: | ||
357 | case R_sub: | ||
358 | case R_mult: | ||
359 | case R_div: | ||
360 | case R_mod: | ||
361 | case R_lshift: | ||
362 | case R_rshift: | ||
363 | case R_and: | ||
364 | case R_or: | ||
365 | case R_xor: | ||
366 | case R_land: | ||
367 | case R_lor: | ||
368 | case R_neg: | ||
369 | case R_comp: | ||
370 | reloc_stack_operate(ELF32_R_TYPE(rel[i].r_info), mod); | ||
371 | break; | ||
372 | default: | ||
373 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
374 | mod->name, ELF32_R_TYPE(rel[i].r_info)); | ||
375 | return -ENOEXEC; | ||
376 | } | ||
377 | } | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | int | ||
382 | module_finalize(const Elf_Ehdr * hdr, | ||
383 | const Elf_Shdr * sechdrs, struct module *mod) | ||
384 | { | ||
385 | unsigned int i, strindex = 0, symindex = 0; | ||
386 | char *secstrings; | ||
387 | |||
388 | secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | ||
389 | |||
390 | for (i = 1; i < hdr->e_shnum; i++) { | ||
391 | /* Internal symbols and strings. */ | ||
392 | if (sechdrs[i].sh_type == SHT_SYMTAB) { | ||
393 | symindex = i; | ||
394 | strindex = sechdrs[i].sh_link; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | for (i = 1; i < hdr->e_shnum; i++) { | ||
399 | const char *strtab = (char *)sechdrs[strindex].sh_addr; | ||
400 | unsigned int info = sechdrs[i].sh_info; | ||
401 | |||
402 | /* Not a valid relocation section? */ | ||
403 | if (info >= hdr->e_shnum) | ||
404 | continue; | ||
405 | |||
406 | if ((sechdrs[i].sh_type == SHT_RELA) && | ||
407 | ((strcmp(".rela.l1.text", secstrings + sechdrs[i].sh_name) == 0)|| | ||
408 | ((strcmp(".rela.text", secstrings + sechdrs[i].sh_name) == 0) && | ||
409 | (hdr->e_flags & FLG_CODE_IN_L1)))) { | ||
410 | apply_relocate_add((Elf_Shdr *) sechdrs, strtab, | ||
411 | symindex, i, mod); | ||
412 | } | ||
413 | } | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | void module_arch_cleanup(struct module *mod) | ||
418 | { | ||
419 | if ((mod->arch.text_l1) && (mod->arch.text_l1->sh_addr)) | ||
420 | l1_inst_sram_free((void*)mod->arch.text_l1->sh_addr); | ||
421 | if ((mod->arch.data_a_l1) && (mod->arch.data_a_l1->sh_addr)) | ||
422 | l1_data_sram_free((void*)mod->arch.data_a_l1->sh_addr); | ||
423 | if ((mod->arch.bss_a_l1) && (mod->arch.bss_a_l1->sh_addr)) | ||
424 | l1_data_sram_free((void*)mod->arch.bss_a_l1->sh_addr); | ||
425 | if ((mod->arch.data_b_l1) && (mod->arch.data_b_l1->sh_addr)) | ||
426 | l1_data_B_sram_free((void*)mod->arch.data_b_l1->sh_addr); | ||
427 | if ((mod->arch.bss_b_l1) && (mod->arch.bss_b_l1->sh_addr)) | ||
428 | l1_data_B_sram_free((void*)mod->arch.bss_b_l1->sh_addr); | ||
429 | } | ||
diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c new file mode 100644 index 000000000000..3eff7439d8d3 --- /dev/null +++ b/arch/blackfin/kernel/process.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/process.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: Blackfin architecture-dependent process handling. | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/smp_lock.h> | ||
32 | #include <linux/unistd.h> | ||
33 | #include <linux/user.h> | ||
34 | #include <linux/a.out.h> | ||
35 | |||
36 | #include <asm/blackfin.h> | ||
37 | #include <asm/uaccess.h> | ||
38 | |||
39 | #define LED_ON 0 | ||
40 | #define LED_OFF 1 | ||
41 | |||
42 | asmlinkage void ret_from_fork(void); | ||
43 | |||
44 | /* Points to the SDRAM backup memory for the stack that is currently in | ||
45 | * L1 scratchpad memory. | ||
46 | */ | ||
47 | void *current_l1_stack_save; | ||
48 | |||
49 | /* The number of tasks currently using a L1 stack area. The SRAM is | ||
50 | * allocated/deallocated whenever this changes from/to zero. | ||
51 | */ | ||
52 | int nr_l1stack_tasks; | ||
53 | |||
54 | /* Start and length of the area in L1 scratchpad memory which we've allocated | ||
55 | * for process stacks. | ||
56 | */ | ||
57 | void *l1_stack_base; | ||
58 | unsigned long l1_stack_len; | ||
59 | |||
60 | /* | ||
61 | * Powermanagement idle function, if any.. | ||
62 | */ | ||
63 | void (*pm_idle)(void) = NULL; | ||
64 | EXPORT_SYMBOL(pm_idle); | ||
65 | |||
66 | void (*pm_power_off)(void) = NULL; | ||
67 | EXPORT_SYMBOL(pm_power_off); | ||
68 | |||
69 | /* | ||
70 | * We are using a different LED from the one used to indicate timer interrupt. | ||
71 | */ | ||
72 | #if defined(CONFIG_BFIN_IDLE_LED) | ||
73 | static inline void leds_switch(int flag) | ||
74 | { | ||
75 | unsigned short tmp = 0; | ||
76 | |||
77 | tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT(); | ||
78 | SSYNC(); | ||
79 | |||
80 | if (flag == LED_ON) | ||
81 | tmp &= ~CONFIG_BFIN_IDLE_LED_PIN; /* light on */ | ||
82 | else | ||
83 | tmp |= CONFIG_BFIN_IDLE_LED_PIN; /* light off */ | ||
84 | |||
85 | bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp); | ||
86 | SSYNC(); | ||
87 | |||
88 | } | ||
89 | #else | ||
90 | static inline void leds_switch(int flag) | ||
91 | { | ||
92 | } | ||
93 | #endif | ||
94 | |||
95 | /* | ||
96 | * The idle loop on BFIN | ||
97 | */ | ||
98 | #ifdef CONFIG_IDLE_L1 | ||
99 | void default_idle(void)__attribute__((l1_text)); | ||
100 | void cpu_idle(void)__attribute__((l1_text)); | ||
101 | #endif | ||
102 | |||
103 | void default_idle(void) | ||
104 | { | ||
105 | while (!need_resched()) { | ||
106 | leds_switch(LED_OFF); | ||
107 | local_irq_disable(); | ||
108 | if (likely(!need_resched())) | ||
109 | idle_with_irq_disabled(); | ||
110 | local_irq_enable(); | ||
111 | leds_switch(LED_ON); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | void (*idle)(void) = default_idle; | ||
116 | |||
117 | /* | ||
118 | * The idle thread. There's no useful work to be | ||
119 | * done, so just try to conserve power and have a | ||
120 | * low exit latency (ie sit in a loop waiting for | ||
121 | * somebody to say that they'd like to reschedule) | ||
122 | */ | ||
123 | void cpu_idle(void) | ||
124 | { | ||
125 | /* endless idle loop with no priority at all */ | ||
126 | while (1) { | ||
127 | idle(); | ||
128 | preempt_enable_no_resched(); | ||
129 | schedule(); | ||
130 | preempt_disable(); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | void machine_restart(char *__unused) | ||
135 | { | ||
136 | #if defined(CONFIG_BLKFIN_CACHE) | ||
137 | bfin_write_IMEM_CONTROL(0x01); | ||
138 | SSYNC(); | ||
139 | #endif | ||
140 | bfin_reset(); | ||
141 | /* Dont do anything till the reset occurs */ | ||
142 | while (1) { | ||
143 | SSYNC(); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | void machine_halt(void) | ||
148 | { | ||
149 | for (;;) | ||
150 | asm volatile ("idle"); | ||
151 | } | ||
152 | |||
153 | void machine_power_off(void) | ||
154 | { | ||
155 | for (;;) | ||
156 | asm volatile ("idle"); | ||
157 | } | ||
158 | |||
159 | void show_regs(struct pt_regs *regs) | ||
160 | { | ||
161 | printk(KERN_NOTICE "\n"); | ||
162 | printk(KERN_NOTICE | ||
163 | "PC: %08lu Status: %04lu SysStatus: %04lu RETS: %08lu\n", | ||
164 | regs->pc, regs->astat, regs->seqstat, regs->rets); | ||
165 | printk(KERN_NOTICE | ||
166 | "A0.x: %08lx A0.w: %08lx A1.x: %08lx A1.w: %08lx\n", | ||
167 | regs->a0x, regs->a0w, regs->a1x, regs->a1w); | ||
168 | printk(KERN_NOTICE "P0: %08lx P1: %08lx P2: %08lx P3: %08lx\n", | ||
169 | regs->p0, regs->p1, regs->p2, regs->p3); | ||
170 | printk(KERN_NOTICE "P4: %08lx P5: %08lx\n", regs->p4, regs->p5); | ||
171 | printk(KERN_NOTICE "R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n", | ||
172 | regs->r0, regs->r1, regs->r2, regs->r3); | ||
173 | printk(KERN_NOTICE "R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n", | ||
174 | regs->r4, regs->r5, regs->r6, regs->r7); | ||
175 | |||
176 | if (!(regs->ipend)) | ||
177 | printk("USP: %08lx\n", rdusp()); | ||
178 | } | ||
179 | |||
180 | /* Fill in the fpu structure for a core dump. */ | ||
181 | |||
182 | int dump_fpu(struct pt_regs *regs, elf_fpregset_t * fpregs) | ||
183 | { | ||
184 | return 1; | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * This gets run with P1 containing the | ||
189 | * function to call, and R1 containing | ||
190 | * the "args". Note P0 is clobbered on the way here. | ||
191 | */ | ||
192 | void kernel_thread_helper(void); | ||
193 | __asm__(".section .text\n" | ||
194 | ".align 4\n" | ||
195 | "_kernel_thread_helper:\n\t" | ||
196 | "\tsp += -12;\n\t" | ||
197 | "\tr0 = r1;\n\t" "\tcall (p1);\n\t" "\tcall _do_exit;\n" ".previous"); | ||
198 | |||
199 | /* | ||
200 | * Create a kernel thread. | ||
201 | */ | ||
202 | pid_t kernel_thread(int (*fn) (void *), void *arg, unsigned long flags) | ||
203 | { | ||
204 | struct pt_regs regs; | ||
205 | |||
206 | memset(®s, 0, sizeof(regs)); | ||
207 | |||
208 | regs.r1 = (unsigned long)arg; | ||
209 | regs.p1 = (unsigned long)fn; | ||
210 | regs.pc = (unsigned long)kernel_thread_helper; | ||
211 | regs.orig_p0 = -1; | ||
212 | /* Set bit 2 to tell ret_from_fork we should be returning to kernel | ||
213 | mode. */ | ||
214 | regs.ipend = 0x8002; | ||
215 | __asm__ __volatile__("%0 = syscfg;":"=da"(regs.syscfg):); | ||
216 | return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, | ||
217 | NULL); | ||
218 | } | ||
219 | |||
220 | void flush_thread(void) | ||
221 | { | ||
222 | } | ||
223 | |||
224 | asmlinkage int bfin_vfork(struct pt_regs *regs) | ||
225 | { | ||
226 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, | ||
227 | NULL); | ||
228 | } | ||
229 | |||
230 | asmlinkage int bfin_clone(struct pt_regs *regs) | ||
231 | { | ||
232 | unsigned long clone_flags; | ||
233 | unsigned long newsp; | ||
234 | |||
235 | /* syscall2 puts clone_flags in r0 and usp in r1 */ | ||
236 | clone_flags = regs->r0; | ||
237 | newsp = regs->r1; | ||
238 | if (!newsp) | ||
239 | newsp = rdusp(); | ||
240 | else | ||
241 | newsp -= 12; | ||
242 | return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); | ||
243 | } | ||
244 | |||
245 | int | ||
246 | copy_thread(int nr, unsigned long clone_flags, | ||
247 | unsigned long usp, unsigned long topstk, | ||
248 | struct task_struct *p, struct pt_regs *regs) | ||
249 | { | ||
250 | struct pt_regs *childregs; | ||
251 | |||
252 | childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1; | ||
253 | *childregs = *regs; | ||
254 | childregs->r0 = 0; | ||
255 | |||
256 | p->thread.usp = usp; | ||
257 | p->thread.ksp = (unsigned long)childregs; | ||
258 | p->thread.pc = (unsigned long)ret_from_fork; | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * fill in the user structure for a core dump.. | ||
265 | */ | ||
266 | void dump_thread(struct pt_regs *regs, struct user *dump) | ||
267 | { | ||
268 | dump->magic = CMAGIC; | ||
269 | dump->start_code = 0; | ||
270 | dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); | ||
271 | dump->u_tsize = ((unsigned long)current->mm->end_code) >> PAGE_SHIFT; | ||
272 | dump->u_dsize = ((unsigned long)(current->mm->brk + | ||
273 | (PAGE_SIZE - 1))) >> PAGE_SHIFT; | ||
274 | dump->u_dsize -= dump->u_tsize; | ||
275 | dump->u_ssize = 0; | ||
276 | |||
277 | if (dump->start_stack < TASK_SIZE) | ||
278 | dump->u_ssize = | ||
279 | ((unsigned long)(TASK_SIZE - | ||
280 | dump->start_stack)) >> PAGE_SHIFT; | ||
281 | |||
282 | dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump); | ||
283 | |||
284 | dump->regs.r0 = regs->r0; | ||
285 | dump->regs.r1 = regs->r1; | ||
286 | dump->regs.r2 = regs->r2; | ||
287 | dump->regs.r3 = regs->r3; | ||
288 | dump->regs.r4 = regs->r4; | ||
289 | dump->regs.r5 = regs->r5; | ||
290 | dump->regs.r6 = regs->r6; | ||
291 | dump->regs.r7 = regs->r7; | ||
292 | dump->regs.p0 = regs->p0; | ||
293 | dump->regs.p1 = regs->p1; | ||
294 | dump->regs.p2 = regs->p2; | ||
295 | dump->regs.p3 = regs->p3; | ||
296 | dump->regs.p4 = regs->p4; | ||
297 | dump->regs.p5 = regs->p5; | ||
298 | dump->regs.orig_p0 = regs->orig_p0; | ||
299 | dump->regs.a0w = regs->a0w; | ||
300 | dump->regs.a1w = regs->a1w; | ||
301 | dump->regs.a0x = regs->a0x; | ||
302 | dump->regs.a1x = regs->a1x; | ||
303 | dump->regs.rets = regs->rets; | ||
304 | dump->regs.astat = regs->astat; | ||
305 | dump->regs.pc = regs->pc; | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * sys_execve() executes a new program. | ||
310 | */ | ||
311 | |||
312 | asmlinkage int sys_execve(char *name, char **argv, char **envp) | ||
313 | { | ||
314 | int error; | ||
315 | char *filename; | ||
316 | struct pt_regs *regs = (struct pt_regs *)((&name) + 6); | ||
317 | |||
318 | lock_kernel(); | ||
319 | filename = getname(name); | ||
320 | error = PTR_ERR(filename); | ||
321 | if (IS_ERR(filename)) | ||
322 | goto out; | ||
323 | error = do_execve(filename, argv, envp, regs); | ||
324 | putname(filename); | ||
325 | out: | ||
326 | unlock_kernel(); | ||
327 | return error; | ||
328 | } | ||
329 | |||
330 | unsigned long get_wchan(struct task_struct *p) | ||
331 | { | ||
332 | unsigned long fp, pc; | ||
333 | unsigned long stack_page; | ||
334 | int count = 0; | ||
335 | if (!p || p == current || p->state == TASK_RUNNING) | ||
336 | return 0; | ||
337 | |||
338 | stack_page = (unsigned long)p; | ||
339 | fp = p->thread.usp; | ||
340 | do { | ||
341 | if (fp < stack_page + sizeof(struct thread_info) || | ||
342 | fp >= 8184 + stack_page) | ||
343 | return 0; | ||
344 | pc = ((unsigned long *)fp)[1]; | ||
345 | if (!in_sched_functions(pc)) | ||
346 | return pc; | ||
347 | fp = *(unsigned long *)fp; | ||
348 | } | ||
349 | while (count++ < 16); | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | #if defined(CONFIG_ACCESS_CHECK) | ||
354 | int _access_ok(unsigned long addr, unsigned long size) | ||
355 | { | ||
356 | |||
357 | if (addr > (addr + size)) | ||
358 | return 0; | ||
359 | if (segment_eq(get_fs(),KERNEL_DS)) | ||
360 | return 1; | ||
361 | #ifdef CONFIG_MTD_UCLINUX | ||
362 | if (addr >= memory_start && (addr + size) <= memory_end) | ||
363 | return 1; | ||
364 | if (addr >= memory_mtd_end && (addr + size) <= physical_mem_end) | ||
365 | return 1; | ||
366 | #else | ||
367 | if (addr >= memory_start && (addr + size) <= physical_mem_end) | ||
368 | return 1; | ||
369 | #endif | ||
370 | if (addr >= (unsigned long)__init_begin && | ||
371 | addr + size <= (unsigned long)__init_end) | ||
372 | return 1; | ||
373 | if (addr >= L1_SCRATCH_START | ||
374 | && addr + size <= L1_SCRATCH_START + L1_SCRATCH_LENGTH) | ||
375 | return 1; | ||
376 | #if L1_CODE_LENGTH != 0 | ||
377 | if (addr >= L1_CODE_START + (_etext_l1 - _stext_l1) | ||
378 | && addr + size <= L1_CODE_START + L1_CODE_LENGTH) | ||
379 | return 1; | ||
380 | #endif | ||
381 | #if L1_DATA_A_LENGTH != 0 | ||
382 | if (addr >= L1_DATA_A_START + (_ebss_l1 - _sdata_l1) | ||
383 | && addr + size <= L1_DATA_A_START + L1_DATA_A_LENGTH) | ||
384 | return 1; | ||
385 | #endif | ||
386 | #if L1_DATA_B_LENGTH != 0 | ||
387 | if (addr >= L1_DATA_B_START | ||
388 | && addr + size <= L1_DATA_B_START + L1_DATA_B_LENGTH) | ||
389 | return 1; | ||
390 | #endif | ||
391 | return 0; | ||
392 | } | ||
393 | EXPORT_SYMBOL(_access_ok); | ||
394 | #endif /* CONFIG_ACCESS_CHECK */ | ||
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c new file mode 100644 index 000000000000..d7c8e514cb92 --- /dev/null +++ b/arch/blackfin/kernel/ptrace.c | |||
@@ -0,0 +1,430 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/ptrace.c | ||
3 | * Based on: Taken from linux/kernel/ptrace.c | ||
4 | * Author: linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds | ||
5 | * | ||
6 | * Created: 1/23/92 | ||
7 | * Description: | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/kernel.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/smp.h> | ||
34 | #include <linux/smp_lock.h> | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/ptrace.h> | ||
37 | #include <linux/user.h> | ||
38 | #include <linux/signal.h> | ||
39 | |||
40 | #include <asm/uaccess.h> | ||
41 | #include <asm/page.h> | ||
42 | #include <asm/pgtable.h> | ||
43 | #include <asm/system.h> | ||
44 | #include <asm/processor.h> | ||
45 | #include <asm/asm-offsets.h> | ||
46 | #include <asm/dma.h> | ||
47 | |||
48 | #define MAX_SHARED_LIBS 3 | ||
49 | #define TEXT_OFFSET 0 | ||
50 | /* | ||
51 | * does not yet catch signals sent when the child dies. | ||
52 | * in exit.c or in signal.c. | ||
53 | */ | ||
54 | |||
55 | /* determines which bits in the SYSCFG reg the user has access to. */ | ||
56 | /* 1 = access 0 = no access */ | ||
57 | #define SYSCFG_MASK 0x0007 /* SYSCFG reg */ | ||
58 | /* sets the trace bits. */ | ||
59 | #define TRACE_BITS 0x0001 | ||
60 | |||
61 | /* Find the stack offset for a register, relative to thread.esp0. */ | ||
62 | #define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) | ||
63 | |||
64 | /* | ||
65 | * Get the address of the live pt_regs for the specified task. | ||
66 | * These are saved onto the top kernel stack when the process | ||
67 | * is not running. | ||
68 | * | ||
69 | * Note: if a user thread is execve'd from kernel space, the | ||
70 | * kernel stack will not be empty on entry to the kernel, so | ||
71 | * ptracing these tasks will fail. | ||
72 | */ | ||
73 | static inline struct pt_regs *get_user_regs(struct task_struct *task) | ||
74 | { | ||
75 | return (struct pt_regs *) | ||
76 | ((unsigned long)task->thread_info + | ||
77 | (THREAD_SIZE - sizeof(struct pt_regs))); | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * Get all user integer registers. | ||
82 | */ | ||
83 | static inline int ptrace_getregs(struct task_struct *tsk, void __user * uregs) | ||
84 | { | ||
85 | struct pt_regs *regs = get_user_regs(tsk); | ||
86 | return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0; | ||
87 | } | ||
88 | |||
89 | /* Mapping from PT_xxx to the stack offset at which the register is | ||
90 | * saved. Notice that usp has no stack-slot and needs to be treated | ||
91 | * specially (see get_reg/put_reg below). | ||
92 | */ | ||
93 | |||
94 | /* | ||
95 | * Get contents of register REGNO in task TASK. | ||
96 | */ | ||
97 | static inline long get_reg(struct task_struct *task, int regno) | ||
98 | { | ||
99 | unsigned char *reg_ptr; | ||
100 | |||
101 | struct pt_regs *regs = | ||
102 | (struct pt_regs *)((unsigned long)task->thread_info + | ||
103 | (THREAD_SIZE - sizeof(struct pt_regs))); | ||
104 | reg_ptr = (char *)regs; | ||
105 | |||
106 | switch (regno) { | ||
107 | case PT_USP: | ||
108 | return task->thread.usp; | ||
109 | default: | ||
110 | if (regno <= 216) | ||
111 | return *(long *)(reg_ptr + regno); | ||
112 | } | ||
113 | /* slight mystery ... never seems to come here but kernel misbehaves without this code! */ | ||
114 | |||
115 | printk(KERN_WARNING "Request to get for unknown register %d\n", regno); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Write contents of register REGNO in task TASK. | ||
121 | */ | ||
122 | static inline int | ||
123 | put_reg(struct task_struct *task, int regno, unsigned long data) | ||
124 | { | ||
125 | char * reg_ptr; | ||
126 | |||
127 | struct pt_regs *regs = | ||
128 | (struct pt_regs *)((unsigned long)task->thread_info + | ||
129 | (THREAD_SIZE - sizeof(struct pt_regs))); | ||
130 | reg_ptr = (char *)regs; | ||
131 | |||
132 | switch (regno) { | ||
133 | case PT_PC: | ||
134 | /*********************************************************************/ | ||
135 | /* At this point the kernel is most likely in exception. */ | ||
136 | /* The RETX register will be used to populate the pc of the process. */ | ||
137 | /*********************************************************************/ | ||
138 | regs->retx = data; | ||
139 | regs->pc = data; | ||
140 | break; | ||
141 | case PT_RETX: | ||
142 | break; /* regs->retx = data; break; */ | ||
143 | case PT_USP: | ||
144 | regs->usp = data; | ||
145 | task->thread.usp = data; | ||
146 | break; | ||
147 | default: | ||
148 | if (regno <= 216) | ||
149 | *(long *)(reg_ptr + regno) = data; | ||
150 | } | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * check that an address falls within the bounds of the target process's memory mappings | ||
156 | */ | ||
157 | static inline int is_user_addr_valid(struct task_struct *child, | ||
158 | unsigned long start, unsigned long len) | ||
159 | { | ||
160 | struct vm_list_struct *vml; | ||
161 | struct sram_list_struct *sraml; | ||
162 | |||
163 | for (vml = child->mm->context.vmlist; vml; vml = vml->next) | ||
164 | if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end) | ||
165 | return 0; | ||
166 | |||
167 | for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next) | ||
168 | if (start >= (unsigned long)sraml->addr | ||
169 | && start + len <= (unsigned long)sraml->addr + sraml->length) | ||
170 | return 0; | ||
171 | |||
172 | return -EIO; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * Called by kernel/ptrace.c when detaching.. | ||
177 | * | ||
178 | * Make sure the single step bit is not set. | ||
179 | */ | ||
180 | void ptrace_disable(struct task_struct *child) | ||
181 | { | ||
182 | unsigned long tmp; | ||
183 | /* make sure the single step bit is not set. */ | ||
184 | tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); | ||
185 | put_reg(child, PT_SR, tmp); | ||
186 | } | ||
187 | |||
188 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | ||
189 | { | ||
190 | int ret; | ||
191 | int add = 0; | ||
192 | |||
193 | switch (request) { | ||
194 | /* when I and D space are separate, these will need to be fixed. */ | ||
195 | case PTRACE_PEEKDATA: | ||
196 | pr_debug("ptrace: PEEKDATA\n"); | ||
197 | add = MAX_SHARED_LIBS * 4; /* space between text and data */ | ||
198 | /* fall through */ | ||
199 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | ||
200 | { | ||
201 | unsigned long tmp = 0; | ||
202 | int copied; | ||
203 | |||
204 | ret = -EIO; | ||
205 | pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + add %d %ld\n", addr, add, | ||
206 | sizeof(data)); | ||
207 | if (is_user_addr_valid(child, addr + add, sizeof(tmp)) < 0) | ||
208 | break; | ||
209 | pr_debug("ptrace: user address is valid\n"); | ||
210 | |||
211 | #if L1_CODE_LENGTH != 0 | ||
212 | if (addr + add >= L1_CODE_START | ||
213 | && addr + add + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) { | ||
214 | safe_dma_memcpy (&tmp, (const void *)(addr + add), sizeof(tmp)); | ||
215 | copied = sizeof(tmp); | ||
216 | } else | ||
217 | #endif | ||
218 | copied = | ||
219 | access_process_vm(child, addr + add, &tmp, | ||
220 | sizeof(tmp), 0); | ||
221 | pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp); | ||
222 | if (copied != sizeof(tmp)) | ||
223 | break; | ||
224 | ret = put_user(tmp, (unsigned long *)data); | ||
225 | break; | ||
226 | } | ||
227 | |||
228 | /* read the word at location addr in the USER area. */ | ||
229 | case PTRACE_PEEKUSR: | ||
230 | { | ||
231 | unsigned long tmp; | ||
232 | ret = -EIO; | ||
233 | tmp = 0; | ||
234 | if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) { | ||
235 | printk(KERN_WARNING "ptrace error : PEEKUSR : temporarily returning " | ||
236 | "0 - %x sizeof(pt_regs) is %lx\n", | ||
237 | (int)addr, sizeof(struct pt_regs)); | ||
238 | break; | ||
239 | } | ||
240 | if (addr == sizeof(struct pt_regs)) { | ||
241 | /* PT_TEXT_ADDR */ | ||
242 | tmp = child->mm->start_code + TEXT_OFFSET; | ||
243 | } else if (addr == (sizeof(struct pt_regs) + 4)) { | ||
244 | /* PT_TEXT_END_ADDR */ | ||
245 | tmp = child->mm->end_code; | ||
246 | } else if (addr == (sizeof(struct pt_regs) + 8)) { | ||
247 | /* PT_DATA_ADDR */ | ||
248 | tmp = child->mm->start_data; | ||
249 | #ifdef CONFIG_BINFMT_ELF_FDPIC | ||
250 | } else if (addr == (sizeof(struct pt_regs) + 12)) { | ||
251 | tmp = child->mm->context.exec_fdpic_loadmap; | ||
252 | } else if (addr == (sizeof(struct pt_regs) + 16)) { | ||
253 | tmp = child->mm->context.interp_fdpic_loadmap; | ||
254 | #endif | ||
255 | } else { | ||
256 | tmp = get_reg(child, addr); | ||
257 | } | ||
258 | ret = put_user(tmp, (unsigned long *)data); | ||
259 | break; | ||
260 | } | ||
261 | |||
262 | /* when I and D space are separate, this will have to be fixed. */ | ||
263 | case PTRACE_POKEDATA: | ||
264 | printk(KERN_NOTICE "ptrace: PTRACE_PEEKDATA\n"); | ||
265 | /* fall through */ | ||
266 | case PTRACE_POKETEXT: /* write the word at location addr. */ | ||
267 | { | ||
268 | int copied; | ||
269 | |||
270 | ret = -EIO; | ||
271 | pr_debug("ptrace: POKETEXT at addr 0x%08lx + add %d %ld bytes %lx\n", | ||
272 | addr, add, sizeof(data), data); | ||
273 | if (is_user_addr_valid(child, addr + add, sizeof(data)) < 0) | ||
274 | break; | ||
275 | pr_debug("ptrace: user address is valid\n"); | ||
276 | |||
277 | #if L1_CODE_LENGTH != 0 | ||
278 | if (addr + add >= L1_CODE_START | ||
279 | && addr + add + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) { | ||
280 | safe_dma_memcpy ((void *)(addr + add), &data, sizeof(data)); | ||
281 | copied = sizeof(data); | ||
282 | } else | ||
283 | #endif | ||
284 | copied = | ||
285 | access_process_vm(child, addr + add, &data, | ||
286 | sizeof(data), 1); | ||
287 | pr_debug("ptrace: copied size %d\n", copied); | ||
288 | if (copied != sizeof(data)) | ||
289 | break; | ||
290 | ret = 0; | ||
291 | break; | ||
292 | } | ||
293 | |||
294 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | ||
295 | ret = -EIO; | ||
296 | if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) { | ||
297 | printk(KERN_WARNING "ptrace error : POKEUSR: temporarily returning 0\n"); | ||
298 | break; | ||
299 | } | ||
300 | |||
301 | if (addr >= (sizeof(struct pt_regs))) { | ||
302 | ret = 0; | ||
303 | break; | ||
304 | } | ||
305 | if (addr == PT_SYSCFG) { | ||
306 | data &= SYSCFG_MASK; | ||
307 | data |= get_reg(child, PT_SYSCFG); | ||
308 | } | ||
309 | ret = put_reg(child, addr, data); | ||
310 | break; | ||
311 | |||
312 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | ||
313 | case PTRACE_CONT: | ||
314 | { /* restart after signal. */ | ||
315 | long tmp; | ||
316 | |||
317 | pr_debug("ptrace_cont\n"); | ||
318 | |||
319 | ret = -EIO; | ||
320 | if (!valid_signal(data)) | ||
321 | break; | ||
322 | if (request == PTRACE_SYSCALL) | ||
323 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
324 | else | ||
325 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
326 | |||
327 | child->exit_code = data; | ||
328 | /* make sure the single step bit is not set. */ | ||
329 | tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS); | ||
330 | put_reg(child, PT_SYSCFG, tmp); | ||
331 | pr_debug("before wake_up_process\n"); | ||
332 | wake_up_process(child); | ||
333 | ret = 0; | ||
334 | break; | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * make the child exit. Best I can do is send it a sigkill. | ||
339 | * perhaps it should be put in the status that it wants to | ||
340 | * exit. | ||
341 | */ | ||
342 | case PTRACE_KILL: | ||
343 | { | ||
344 | long tmp; | ||
345 | ret = 0; | ||
346 | if (child->exit_state == EXIT_ZOMBIE) /* already dead */ | ||
347 | break; | ||
348 | child->exit_code = SIGKILL; | ||
349 | /* make sure the single step bit is not set. */ | ||
350 | tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS); | ||
351 | put_reg(child, PT_SYSCFG, tmp); | ||
352 | wake_up_process(child); | ||
353 | break; | ||
354 | } | ||
355 | |||
356 | case PTRACE_SINGLESTEP: | ||
357 | { /* set the trap flag. */ | ||
358 | long tmp; | ||
359 | |||
360 | pr_debug("single step\n"); | ||
361 | ret = -EIO; | ||
362 | if (!valid_signal(data)) | ||
363 | break; | ||
364 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
365 | |||
366 | tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS); | ||
367 | put_reg(child, PT_SYSCFG, tmp); | ||
368 | |||
369 | child->exit_code = data; | ||
370 | /* give it a chance to run. */ | ||
371 | wake_up_process(child); | ||
372 | ret = 0; | ||
373 | break; | ||
374 | } | ||
375 | |||
376 | case PTRACE_DETACH: | ||
377 | { /* detach a process that was attached. */ | ||
378 | ret = ptrace_detach(child, data); | ||
379 | break; | ||
380 | } | ||
381 | |||
382 | case PTRACE_GETREGS: | ||
383 | { | ||
384 | |||
385 | /* Get all gp regs from the child. */ | ||
386 | ret = ptrace_getregs(child, (void __user *)data); | ||
387 | break; | ||
388 | } | ||
389 | |||
390 | case PTRACE_SETREGS: | ||
391 | { | ||
392 | printk(KERN_NOTICE | ||
393 | "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n"); | ||
394 | /* Set all gp regs in the child. */ | ||
395 | ret = 0; | ||
396 | break; | ||
397 | } | ||
398 | default: | ||
399 | ret = ptrace_request(child, request, addr, data); | ||
400 | break; | ||
401 | } | ||
402 | |||
403 | return ret; | ||
404 | } | ||
405 | |||
406 | asmlinkage void syscall_trace(void) | ||
407 | { | ||
408 | |||
409 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
410 | return; | ||
411 | |||
412 | if (!(current->ptrace & PT_PTRACED)) | ||
413 | return; | ||
414 | |||
415 | /* the 0x80 provides a way for the tracing parent to distinguish | ||
416 | * between a syscall stop and SIGTRAP delivery | ||
417 | */ | ||
418 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
419 | ? 0x80 : 0)); | ||
420 | |||
421 | /* | ||
422 | * this isn't the same as continuing with a signal, but it will do | ||
423 | * for normal use. strace only continues with a signal if the | ||
424 | * stopping signal is not SIGTRAP. -brl | ||
425 | */ | ||
426 | if (current->exit_code) { | ||
427 | send_sig(current->exit_code, current, 1); | ||
428 | current->exit_code = 0; | ||
429 | } | ||
430 | } | ||
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c new file mode 100644 index 000000000000..342bb8dd56ac --- /dev/null +++ b/arch/blackfin/kernel/setup.c | |||
@@ -0,0 +1,902 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/setup.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/delay.h> | ||
31 | #include <linux/console.h> | ||
32 | #include <linux/bootmem.h> | ||
33 | #include <linux/seq_file.h> | ||
34 | #include <linux/cpu.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/console.h> | ||
37 | #include <linux/tty.h> | ||
38 | |||
39 | #include <linux/ext2_fs.h> | ||
40 | #include <linux/cramfs_fs.h> | ||
41 | #include <linux/romfs_fs.h> | ||
42 | |||
43 | #include <asm/cacheflush.h> | ||
44 | #include <asm/blackfin.h> | ||
45 | #include <asm/cplbinit.h> | ||
46 | |||
47 | unsigned long memory_start, memory_end, physical_mem_end; | ||
48 | unsigned long reserved_mem_dcache_on; | ||
49 | unsigned long reserved_mem_icache_on; | ||
50 | EXPORT_SYMBOL(memory_start); | ||
51 | EXPORT_SYMBOL(memory_end); | ||
52 | EXPORT_SYMBOL(physical_mem_end); | ||
53 | EXPORT_SYMBOL(_ramend); | ||
54 | |||
55 | #ifdef CONFIG_MTD_UCLINUX | ||
56 | unsigned long memory_mtd_end, memory_mtd_start, mtd_size; | ||
57 | unsigned long _ebss; | ||
58 | EXPORT_SYMBOL(memory_mtd_end); | ||
59 | EXPORT_SYMBOL(memory_mtd_start); | ||
60 | EXPORT_SYMBOL(mtd_size); | ||
61 | #endif | ||
62 | |||
63 | char command_line[COMMAND_LINE_SIZE]; | ||
64 | |||
65 | #if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE) | ||
66 | static void generate_cpl_tables(void); | ||
67 | #endif | ||
68 | |||
69 | void __init bf53x_cache_init(void) | ||
70 | { | ||
71 | #if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE) | ||
72 | generate_cpl_tables(); | ||
73 | #endif | ||
74 | |||
75 | #ifdef CONFIG_BLKFIN_CACHE | ||
76 | bfin_icache_init(); | ||
77 | printk(KERN_INFO "Instruction Cache Enabled\n"); | ||
78 | #endif | ||
79 | |||
80 | #ifdef CONFIG_BLKFIN_DCACHE | ||
81 | bfin_dcache_init(); | ||
82 | printk(KERN_INFO "Data Cache Enabled" | ||
83 | # if defined CONFIG_BLKFIN_WB | ||
84 | " (write-back)" | ||
85 | # elif defined CONFIG_BLKFIN_WT | ||
86 | " (write-through)" | ||
87 | # endif | ||
88 | "\n"); | ||
89 | #endif | ||
90 | } | ||
91 | |||
92 | void bf53x_relocate_l1_mem(void) | ||
93 | { | ||
94 | unsigned long l1_code_length; | ||
95 | unsigned long l1_data_a_length; | ||
96 | unsigned long l1_data_b_length; | ||
97 | |||
98 | l1_code_length = _etext_l1 - _stext_l1; | ||
99 | if (l1_code_length > L1_CODE_LENGTH) | ||
100 | l1_code_length = L1_CODE_LENGTH; | ||
101 | /* cannot complain as printk is not available as yet. | ||
102 | * But we can continue booting and complain later! | ||
103 | */ | ||
104 | |||
105 | /* Copy _stext_l1 to _etext_l1 to L1 instruction SRAM */ | ||
106 | dma_memcpy(_stext_l1, _l1_lma_start, l1_code_length); | ||
107 | |||
108 | l1_data_a_length = _ebss_l1 - _sdata_l1; | ||
109 | if (l1_data_a_length > L1_DATA_A_LENGTH) | ||
110 | l1_data_a_length = L1_DATA_A_LENGTH; | ||
111 | |||
112 | /* Copy _sdata_l1 to _ebss_l1 to L1 data bank A SRAM */ | ||
113 | dma_memcpy(_sdata_l1, _l1_lma_start + l1_code_length, l1_data_a_length); | ||
114 | |||
115 | l1_data_b_length = _ebss_b_l1 - _sdata_b_l1; | ||
116 | if (l1_data_b_length > L1_DATA_B_LENGTH) | ||
117 | l1_data_b_length = L1_DATA_B_LENGTH; | ||
118 | |||
119 | /* Copy _sdata_b_l1 to _ebss_b_l1 to L1 data bank B SRAM */ | ||
120 | dma_memcpy(_sdata_b_l1, _l1_lma_start + l1_code_length + | ||
121 | l1_data_a_length, l1_data_b_length); | ||
122 | |||
123 | } | ||
124 | |||
125 | /* | ||
126 | * Initial parsing of the command line. Currently, we support: | ||
127 | * - Controlling the linux memory size: mem=xxx[KMG] | ||
128 | * - Controlling the physical memory size: max_mem=xxx[KMG][$][#] | ||
129 | * $ -> reserved memory is dcacheable | ||
130 | * # -> reserved memory is icacheable | ||
131 | */ | ||
132 | static __init void parse_cmdline_early(char *cmdline_p) | ||
133 | { | ||
134 | char c = ' ', *to = cmdline_p; | ||
135 | unsigned int memsize; | ||
136 | for (;;) { | ||
137 | if (c == ' ') { | ||
138 | |||
139 | if (!memcmp(to, "mem=", 4)) { | ||
140 | to += 4; | ||
141 | memsize = memparse(to, &to); | ||
142 | if (memsize) | ||
143 | _ramend = memsize; | ||
144 | |||
145 | } else if (!memcmp(to, "max_mem=", 8)) { | ||
146 | to += 8; | ||
147 | memsize = memparse(to, &to); | ||
148 | if (memsize) { | ||
149 | physical_mem_end = memsize; | ||
150 | if (*to != ' ') { | ||
151 | if (*to == '$' | ||
152 | || *(to + 1) == '$') | ||
153 | reserved_mem_dcache_on = | ||
154 | 1; | ||
155 | if (*to == '#' | ||
156 | || *(to + 1) == '#') | ||
157 | reserved_mem_icache_on = | ||
158 | 1; | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | } | ||
164 | c = *(to++); | ||
165 | if (!c) | ||
166 | break; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | void __init setup_arch(char **cmdline_p) | ||
171 | { | ||
172 | int bootmap_size; | ||
173 | unsigned long l1_length, sclk, cclk; | ||
174 | #ifdef CONFIG_MTD_UCLINUX | ||
175 | unsigned long mtd_phys = 0; | ||
176 | #endif | ||
177 | |||
178 | cclk = get_cclk(); | ||
179 | sclk = get_sclk(); | ||
180 | |||
181 | #if !defined(CONFIG_BFIN_KERNEL_CLOCK) && defined(ANOMALY_05000273) | ||
182 | if (cclk == sclk) | ||
183 | panic("ANOMALY 05000273, SCLK can not be same as CCLK"); | ||
184 | #endif | ||
185 | |||
186 | #if defined(ANOMALY_05000266) | ||
187 | bfin_read_IMDMA_D0_IRQ_STATUS(); | ||
188 | bfin_read_IMDMA_D1_IRQ_STATUS(); | ||
189 | #endif | ||
190 | |||
191 | #ifdef DEBUG_SERIAL_EARLY_INIT | ||
192 | bfin_console_init(); /* early console registration */ | ||
193 | /* this give a chance to get printk() working before crash. */ | ||
194 | #endif | ||
195 | |||
196 | #if defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH) | ||
197 | /* we need to initialize the Flashrom device here since we might | ||
198 | * do things with flash early on in the boot | ||
199 | */ | ||
200 | flash_probe(); | ||
201 | #endif | ||
202 | |||
203 | #if defined(CONFIG_CMDLINE_BOOL) | ||
204 | memset(command_line, 0, sizeof(command_line)); | ||
205 | strncpy(&command_line[0], CONFIG_CMDLINE, sizeof(command_line)); | ||
206 | command_line[sizeof(command_line) - 1] = 0; | ||
207 | #endif | ||
208 | |||
209 | /* Keep a copy of command line */ | ||
210 | *cmdline_p = &command_line[0]; | ||
211 | memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); | ||
212 | boot_command_line[COMMAND_LINE_SIZE - 1] = 0; | ||
213 | |||
214 | /* setup memory defaults from the user config */ | ||
215 | physical_mem_end = 0; | ||
216 | _ramend = CONFIG_MEM_SIZE * 1024 * 1024; | ||
217 | |||
218 | parse_cmdline_early(&command_line[0]); | ||
219 | |||
220 | if (physical_mem_end == 0) | ||
221 | physical_mem_end = _ramend; | ||
222 | |||
223 | /* by now the stack is part of the init task */ | ||
224 | memory_end = _ramend - DMA_UNCACHED_REGION; | ||
225 | |||
226 | _ramstart = (unsigned long)__bss_stop; | ||
227 | memory_start = PAGE_ALIGN(_ramstart); | ||
228 | |||
229 | #if defined(CONFIG_MTD_UCLINUX) | ||
230 | /* generic memory mapped MTD driver */ | ||
231 | memory_mtd_end = memory_end; | ||
232 | |||
233 | mtd_phys = _ramstart; | ||
234 | mtd_size = PAGE_ALIGN(*((unsigned long *)(mtd_phys + 8))); | ||
235 | |||
236 | # if defined(CONFIG_EXT2_FS) || defined(CONFIG_EXT3_FS) | ||
237 | if (*((unsigned short *)(mtd_phys + 0x438)) == EXT2_SUPER_MAGIC) | ||
238 | mtd_size = | ||
239 | PAGE_ALIGN(*((unsigned long *)(mtd_phys + 0x404)) << 10); | ||
240 | # endif | ||
241 | |||
242 | # if defined(CONFIG_CRAMFS) | ||
243 | if (*((unsigned long *)(mtd_phys)) == CRAMFS_MAGIC) | ||
244 | mtd_size = PAGE_ALIGN(*((unsigned long *)(mtd_phys + 0x4))); | ||
245 | # endif | ||
246 | |||
247 | # if defined(CONFIG_ROMFS_FS) | ||
248 | if (((unsigned long *)mtd_phys)[0] == ROMSB_WORD0 | ||
249 | && ((unsigned long *)mtd_phys)[1] == ROMSB_WORD1) | ||
250 | mtd_size = | ||
251 | PAGE_ALIGN(be32_to_cpu(((unsigned long *)mtd_phys)[2])); | ||
252 | # if (defined(CONFIG_BLKFIN_CACHE) && defined(ANOMALY_05000263)) | ||
253 | /* Due to a Hardware Anomaly we need to limit the size of usable | ||
254 | * instruction memory to max 60MB, 56 if HUNT_FOR_ZERO is on | ||
255 | * 05000263 - Hardware loop corrupted when taking an ICPLB exception | ||
256 | */ | ||
257 | # if (defined(CONFIG_DEBUG_HUNT_FOR_ZERO)) | ||
258 | if (memory_end >= 56 * 1024 * 1024) | ||
259 | memory_end = 56 * 1024 * 1024; | ||
260 | # else | ||
261 | if (memory_end >= 60 * 1024 * 1024) | ||
262 | memory_end = 60 * 1024 * 1024; | ||
263 | # endif /* CONFIG_DEBUG_HUNT_FOR_ZERO */ | ||
264 | # endif /* ANOMALY_05000263 */ | ||
265 | # endif /* CONFIG_ROMFS_FS */ | ||
266 | |||
267 | memory_end -= mtd_size; | ||
268 | |||
269 | if (mtd_size == 0) { | ||
270 | console_init(); | ||
271 | panic("Don't boot kernel without rootfs attached.\n"); | ||
272 | } | ||
273 | |||
274 | /* Relocate MTD image to the top of memory after the uncached memory area */ | ||
275 | dma_memcpy((char *)memory_end, __bss_stop, mtd_size); | ||
276 | |||
277 | memory_mtd_start = memory_end; | ||
278 | _ebss = memory_mtd_start; /* define _ebss for compatible */ | ||
279 | #endif /* CONFIG_MTD_UCLINUX */ | ||
280 | |||
281 | #if (defined(CONFIG_BLKFIN_CACHE) && defined(ANOMALY_05000263)) | ||
282 | /* Due to a Hardware Anomaly we need to limit the size of usable | ||
283 | * instruction memory to max 60MB, 56 if HUNT_FOR_ZERO is on | ||
284 | * 05000263 - Hardware loop corrupted when taking an ICPLB exception | ||
285 | */ | ||
286 | #if (defined(CONFIG_DEBUG_HUNT_FOR_ZERO)) | ||
287 | if (memory_end >= 56 * 1024 * 1024) | ||
288 | memory_end = 56 * 1024 * 1024; | ||
289 | #else | ||
290 | if (memory_end >= 60 * 1024 * 1024) | ||
291 | memory_end = 60 * 1024 * 1024; | ||
292 | #endif /* CONFIG_DEBUG_HUNT_FOR_ZERO */ | ||
293 | printk(KERN_NOTICE "Warning: limiting memory to %liMB due to hardware anomaly 05000263\n", memory_end >> 20); | ||
294 | #endif /* ANOMALY_05000263 */ | ||
295 | |||
296 | #if !defined(CONFIG_MTD_UCLINUX) | ||
297 | memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ | ||
298 | #endif | ||
299 | init_mm.start_code = (unsigned long)_stext; | ||
300 | init_mm.end_code = (unsigned long)_etext; | ||
301 | init_mm.end_data = (unsigned long)_edata; | ||
302 | init_mm.brk = (unsigned long)0; | ||
303 | |||
304 | init_leds(); | ||
305 | |||
306 | printk(KERN_INFO "Blackfin support (C) 2004-2007 Analog Devices, Inc.\n"); | ||
307 | printk(KERN_INFO "Compiled for ADSP-%s Rev 0.%d\n", CPU, bfin_compiled_revid()); | ||
308 | if (bfin_revid() != bfin_compiled_revid()) | ||
309 | printk(KERN_ERR "Warning: Compiled for Rev %d, but running on Rev %d\n", | ||
310 | bfin_compiled_revid(), bfin_revid()); | ||
311 | if (bfin_revid() < SUPPORTED_REVID) | ||
312 | printk(KERN_ERR "Warning: Unsupported Chip Revision ADSP-%s Rev 0.%d detected\n", | ||
313 | CPU, bfin_revid()); | ||
314 | printk(KERN_INFO "Blackfin Linux support by http://blackfin.uclinux.org/\n"); | ||
315 | |||
316 | printk(KERN_INFO "Processor Speed: %lu MHz core clock and %lu Mhz System Clock\n", | ||
317 | cclk / 1000000, sclk / 1000000); | ||
318 | |||
319 | #if defined(ANOMALY_05000273) | ||
320 | if ((cclk >> 1) <= sclk) | ||
321 | printk("\n\n\nANOMALY_05000273: CCLK must be >= 2*SCLK !!!\n\n\n"); | ||
322 | #endif | ||
323 | |||
324 | printk(KERN_INFO "Board Memory: %ldMB\n", physical_mem_end >> 20); | ||
325 | printk(KERN_INFO "Kernel Managed Memory: %ldMB\n", _ramend >> 20); | ||
326 | |||
327 | printk(KERN_INFO "Memory map:\n" | ||
328 | KERN_INFO " text = 0x%p-0x%p\n" | ||
329 | KERN_INFO " init = 0x%p-0x%p\n" | ||
330 | KERN_INFO " data = 0x%p-0x%p\n" | ||
331 | KERN_INFO " stack = 0x%p-0x%p\n" | ||
332 | KERN_INFO " bss = 0x%p-0x%p\n" | ||
333 | KERN_INFO " available = 0x%p-0x%p\n" | ||
334 | #ifdef CONFIG_MTD_UCLINUX | ||
335 | KERN_INFO " rootfs = 0x%p-0x%p\n" | ||
336 | #endif | ||
337 | #if DMA_UNCACHED_REGION > 0 | ||
338 | KERN_INFO " DMA Zone = 0x%p-0x%p\n" | ||
339 | #endif | ||
340 | , _stext, _etext, | ||
341 | __init_begin, __init_end, | ||
342 | _sdata, _edata, | ||
343 | (void*)&init_thread_union, (void*)((int)(&init_thread_union) + 0x2000), | ||
344 | __bss_start, __bss_stop, | ||
345 | (void*)_ramstart, (void*)memory_end | ||
346 | #ifdef CONFIG_MTD_UCLINUX | ||
347 | , (void*)memory_mtd_start, (void*)(memory_mtd_start + mtd_size) | ||
348 | #endif | ||
349 | #if DMA_UNCACHED_REGION > 0 | ||
350 | , (void*)(_ramend - DMA_UNCACHED_REGION), (void*)(_ramend) | ||
351 | #endif | ||
352 | ); | ||
353 | |||
354 | /* | ||
355 | * give all the memory to the bootmap allocator, tell it to put the | ||
356 | * boot mem_map at the start of memory | ||
357 | */ | ||
358 | bootmap_size = init_bootmem_node(NODE_DATA(0), memory_start >> PAGE_SHIFT, /* map goes here */ | ||
359 | PAGE_OFFSET >> PAGE_SHIFT, | ||
360 | memory_end >> PAGE_SHIFT); | ||
361 | /* | ||
362 | * free the usable memory, we have to make sure we do not free | ||
363 | * the bootmem bitmap so we then reserve it after freeing it :-) | ||
364 | */ | ||
365 | free_bootmem(memory_start, memory_end - memory_start); | ||
366 | |||
367 | reserve_bootmem(memory_start, bootmap_size); | ||
368 | /* | ||
369 | * get kmalloc into gear | ||
370 | */ | ||
371 | paging_init(); | ||
372 | |||
373 | /* check the size of the l1 area */ | ||
374 | l1_length = _etext_l1 - _stext_l1; | ||
375 | if (l1_length > L1_CODE_LENGTH) | ||
376 | panic("L1 memory overflow\n"); | ||
377 | |||
378 | l1_length = _ebss_l1 - _sdata_l1; | ||
379 | if (l1_length > L1_DATA_A_LENGTH) | ||
380 | panic("L1 memory overflow\n"); | ||
381 | |||
382 | bf53x_cache_init(); | ||
383 | |||
384 | #if defined(CONFIG_SMC91X) || defined(CONFIG_SMC91X_MODULE) | ||
385 | # if defined(CONFIG_BFIN_SHARED_FLASH_ENET) && defined(CONFIG_BFIN533_STAMP) | ||
386 | /* setup BF533_STAMP CPLD to route AMS3 to Ethernet MAC */ | ||
387 | bfin_write_FIO_DIR(bfin_read_FIO_DIR() | (1 << CONFIG_ENET_FLASH_PIN)); | ||
388 | bfin_write_FIO_FLAG_S(1 << CONFIG_ENET_FLASH_PIN); | ||
389 | SSYNC(); | ||
390 | # endif | ||
391 | # if defined (CONFIG_BFIN561_EZKIT) | ||
392 | bfin_write_FIO0_DIR(bfin_read_FIO0_DIR() | (1 << 12)); | ||
393 | SSYNC(); | ||
394 | # endif /* defined (CONFIG_BFIN561_EZKIT) */ | ||
395 | #endif | ||
396 | |||
397 | printk(KERN_INFO "Hardware Trace Enabled\n"); | ||
398 | bfin_write_TBUFCTL(0x03); | ||
399 | } | ||
400 | |||
401 | #if defined(CONFIG_BF561) | ||
402 | static struct cpu cpu[2]; | ||
403 | #else | ||
404 | static struct cpu cpu[1]; | ||
405 | #endif | ||
406 | static int __init topology_init(void) | ||
407 | { | ||
408 | #if defined (CONFIG_BF561) | ||
409 | register_cpu(&cpu[0], 0); | ||
410 | register_cpu(&cpu[1], 1); | ||
411 | return 0; | ||
412 | #else | ||
413 | return register_cpu(cpu, 0); | ||
414 | #endif | ||
415 | } | ||
416 | |||
417 | subsys_initcall(topology_init); | ||
418 | |||
419 | #if defined(CONFIG_BLKFIN_DCACHE) || defined(CONFIG_BLKFIN_CACHE) | ||
420 | u16 lock_kernel_check(u32 start, u32 end) | ||
421 | { | ||
422 | if ((start <= (u32) _stext && end >= (u32) _end) | ||
423 | || (start >= (u32) _stext && end <= (u32) _end)) | ||
424 | return IN_KERNEL; | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static unsigned short __init | ||
429 | fill_cplbtab(struct cplb_tab *table, | ||
430 | unsigned long start, unsigned long end, | ||
431 | unsigned long block_size, unsigned long cplb_data) | ||
432 | { | ||
433 | int i; | ||
434 | |||
435 | switch (block_size) { | ||
436 | case SIZE_4M: | ||
437 | i = 3; | ||
438 | break; | ||
439 | case SIZE_1M: | ||
440 | i = 2; | ||
441 | break; | ||
442 | case SIZE_4K: | ||
443 | i = 1; | ||
444 | break; | ||
445 | case SIZE_1K: | ||
446 | default: | ||
447 | i = 0; | ||
448 | break; | ||
449 | } | ||
450 | |||
451 | cplb_data = (cplb_data & ~(3 << 16)) | (i << 16); | ||
452 | |||
453 | while ((start < end) && (table->pos < table->size)) { | ||
454 | |||
455 | table->tab[table->pos++] = start; | ||
456 | |||
457 | if (lock_kernel_check(start, start + block_size) == IN_KERNEL) | ||
458 | table->tab[table->pos++] = | ||
459 | cplb_data | CPLB_LOCK | CPLB_DIRTY; | ||
460 | else | ||
461 | table->tab[table->pos++] = cplb_data; | ||
462 | |||
463 | start += block_size; | ||
464 | } | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | static unsigned short __init | ||
469 | close_cplbtab(struct cplb_tab *table) | ||
470 | { | ||
471 | |||
472 | while (table->pos < table->size) { | ||
473 | |||
474 | table->tab[table->pos++] = 0; | ||
475 | table->tab[table->pos++] = 0; /* !CPLB_VALID */ | ||
476 | } | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | static void __init generate_cpl_tables(void) | ||
481 | { | ||
482 | |||
483 | u16 i, j, process; | ||
484 | u32 a_start, a_end, as, ae, as_1m; | ||
485 | |||
486 | struct cplb_tab *t_i = NULL; | ||
487 | struct cplb_tab *t_d = NULL; | ||
488 | struct s_cplb cplb; | ||
489 | |||
490 | cplb.init_i.size = MAX_CPLBS; | ||
491 | cplb.init_d.size = MAX_CPLBS; | ||
492 | cplb.switch_i.size = MAX_SWITCH_I_CPLBS; | ||
493 | cplb.switch_d.size = MAX_SWITCH_D_CPLBS; | ||
494 | |||
495 | cplb.init_i.pos = 0; | ||
496 | cplb.init_d.pos = 0; | ||
497 | cplb.switch_i.pos = 0; | ||
498 | cplb.switch_d.pos = 0; | ||
499 | |||
500 | cplb.init_i.tab = icplb_table; | ||
501 | cplb.init_d.tab = dcplb_table; | ||
502 | cplb.switch_i.tab = ipdt_table; | ||
503 | cplb.switch_d.tab = dpdt_table; | ||
504 | |||
505 | cplb_data[SDRAM_KERN].end = memory_end; | ||
506 | |||
507 | #ifdef CONFIG_MTD_UCLINUX | ||
508 | cplb_data[SDRAM_RAM_MTD].start = memory_mtd_start; | ||
509 | cplb_data[SDRAM_RAM_MTD].end = memory_mtd_start + mtd_size; | ||
510 | cplb_data[SDRAM_RAM_MTD].valid = mtd_size > 0; | ||
511 | # if defined(CONFIG_ROMFS_FS) | ||
512 | cplb_data[SDRAM_RAM_MTD].attr |= I_CPLB; | ||
513 | |||
514 | /* | ||
515 | * The ROMFS_FS size is often not multiple of 1MB. | ||
516 | * This can cause multiple CPLB sets covering the same memory area. | ||
517 | * This will then cause multiple CPLB hit exceptions. | ||
518 | * Workaround: We ensure a contiguous memory area by extending the kernel | ||
519 | * memory section over the mtd section. | ||
520 | * For ROMFS_FS memory must be covered with ICPLBs anyways. | ||
521 | * So there is no difference between kernel and mtd memory setup. | ||
522 | */ | ||
523 | |||
524 | cplb_data[SDRAM_KERN].end = memory_mtd_start + mtd_size;; | ||
525 | cplb_data[SDRAM_RAM_MTD].valid = 0; | ||
526 | |||
527 | # endif | ||
528 | #else | ||
529 | cplb_data[SDRAM_RAM_MTD].valid = 0; | ||
530 | #endif | ||
531 | |||
532 | cplb_data[SDRAM_DMAZ].start = _ramend - DMA_UNCACHED_REGION; | ||
533 | cplb_data[SDRAM_DMAZ].end = _ramend; | ||
534 | |||
535 | cplb_data[RES_MEM].start = _ramend; | ||
536 | cplb_data[RES_MEM].end = physical_mem_end; | ||
537 | |||
538 | if (reserved_mem_dcache_on) | ||
539 | cplb_data[RES_MEM].d_conf = SDRAM_DGENERIC; | ||
540 | else | ||
541 | cplb_data[RES_MEM].d_conf = SDRAM_DNON_CHBL; | ||
542 | |||
543 | if (reserved_mem_icache_on) | ||
544 | cplb_data[RES_MEM].i_conf = SDRAM_IGENERIC; | ||
545 | else | ||
546 | cplb_data[RES_MEM].i_conf = SDRAM_INON_CHBL; | ||
547 | |||
548 | for (i = ZERO_P; i <= L2_MEM; i++) { | ||
549 | |||
550 | if (cplb_data[i].valid) { | ||
551 | |||
552 | as_1m = cplb_data[i].start % SIZE_1M; | ||
553 | |||
554 | /* We need to make sure all sections are properly 1M aligned | ||
555 | * However between Kernel Memory and the Kernel mtd section, depending on the | ||
556 | * rootfs size, there can be overlapping memory areas. | ||
557 | */ | ||
558 | |||
559 | if (as_1m && i!=L1I_MEM && i!=L1D_MEM) { | ||
560 | #ifdef CONFIG_MTD_UCLINUX | ||
561 | if (i == SDRAM_RAM_MTD) { | ||
562 | if ((cplb_data[SDRAM_KERN].end + 1) > cplb_data[SDRAM_RAM_MTD].start) | ||
563 | cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)) + SIZE_1M; | ||
564 | else | ||
565 | cplb_data[SDRAM_RAM_MTD].start = (cplb_data[i].start & (-2*SIZE_1M)); | ||
566 | } else | ||
567 | #endif | ||
568 | printk(KERN_WARNING "Unaligned Start of %s at 0x%X\n", | ||
569 | cplb_data[i].name, cplb_data[i].start); | ||
570 | } | ||
571 | |||
572 | as = cplb_data[i].start % SIZE_4M; | ||
573 | ae = cplb_data[i].end % SIZE_4M; | ||
574 | |||
575 | if (as) | ||
576 | a_start = cplb_data[i].start + (SIZE_4M - (as)); | ||
577 | else | ||
578 | a_start = cplb_data[i].start; | ||
579 | |||
580 | a_end = cplb_data[i].end - ae; | ||
581 | |||
582 | for (j = INITIAL_T; j <= SWITCH_T; j++) { | ||
583 | |||
584 | switch (j) { | ||
585 | case INITIAL_T: | ||
586 | if (cplb_data[i].attr & INITIAL_T) { | ||
587 | t_i = &cplb.init_i; | ||
588 | t_d = &cplb.init_d; | ||
589 | process = 1; | ||
590 | } else | ||
591 | process = 0; | ||
592 | break; | ||
593 | case SWITCH_T: | ||
594 | if (cplb_data[i].attr & SWITCH_T) { | ||
595 | t_i = &cplb.switch_i; | ||
596 | t_d = &cplb.switch_d; | ||
597 | process = 1; | ||
598 | } else | ||
599 | process = 0; | ||
600 | break; | ||
601 | default: | ||
602 | process = 0; | ||
603 | break; | ||
604 | } | ||
605 | |||
606 | if (process) { | ||
607 | if (cplb_data[i].attr & I_CPLB) { | ||
608 | |||
609 | if (cplb_data[i].psize) { | ||
610 | fill_cplbtab(t_i, | ||
611 | cplb_data[i].start, | ||
612 | cplb_data[i].end, | ||
613 | cplb_data[i].psize, | ||
614 | cplb_data[i].i_conf); | ||
615 | } else { | ||
616 | /*icplb_table */ | ||
617 | #if (defined(CONFIG_BLKFIN_CACHE) && defined(ANOMALY_05000263)) | ||
618 | if (i == SDRAM_KERN) { | ||
619 | fill_cplbtab(t_i, | ||
620 | cplb_data[i].start, | ||
621 | cplb_data[i].end, | ||
622 | SIZE_4M, | ||
623 | cplb_data[i].i_conf); | ||
624 | } else | ||
625 | #endif | ||
626 | { | ||
627 | fill_cplbtab(t_i, | ||
628 | cplb_data[i].start, | ||
629 | a_start, | ||
630 | SIZE_1M, | ||
631 | cplb_data[i].i_conf); | ||
632 | fill_cplbtab(t_i, | ||
633 | a_start, | ||
634 | a_end, | ||
635 | SIZE_4M, | ||
636 | cplb_data[i].i_conf); | ||
637 | fill_cplbtab(t_i, a_end, | ||
638 | cplb_data[i].end, | ||
639 | SIZE_1M, | ||
640 | cplb_data[i].i_conf); | ||
641 | } | ||
642 | } | ||
643 | |||
644 | } | ||
645 | if (cplb_data[i].attr & D_CPLB) { | ||
646 | |||
647 | if (cplb_data[i].psize) { | ||
648 | fill_cplbtab(t_d, | ||
649 | cplb_data[i].start, | ||
650 | cplb_data[i].end, | ||
651 | cplb_data[i].psize, | ||
652 | cplb_data[i].d_conf); | ||
653 | } else { | ||
654 | /*dcplb_table*/ | ||
655 | fill_cplbtab(t_d, | ||
656 | cplb_data[i].start, | ||
657 | a_start, SIZE_1M, | ||
658 | cplb_data[i].d_conf); | ||
659 | fill_cplbtab(t_d, a_start, | ||
660 | a_end, SIZE_4M, | ||
661 | cplb_data[i].d_conf); | ||
662 | fill_cplbtab(t_d, a_end, | ||
663 | cplb_data[i].end, | ||
664 | SIZE_1M, | ||
665 | cplb_data[i].d_conf); | ||
666 | |||
667 | } | ||
668 | |||
669 | } | ||
670 | } | ||
671 | } | ||
672 | |||
673 | } | ||
674 | } | ||
675 | |||
676 | /* close tables */ | ||
677 | |||
678 | close_cplbtab(&cplb.init_i); | ||
679 | close_cplbtab(&cplb.init_d); | ||
680 | |||
681 | cplb.init_i.tab[cplb.init_i.pos] = -1; | ||
682 | cplb.init_d.tab[cplb.init_d.pos] = -1; | ||
683 | cplb.switch_i.tab[cplb.switch_i.pos] = -1; | ||
684 | cplb.switch_d.tab[cplb.switch_d.pos] = -1; | ||
685 | |||
686 | } | ||
687 | |||
688 | #endif | ||
689 | |||
690 | static inline u_long get_vco(void) | ||
691 | { | ||
692 | u_long msel; | ||
693 | u_long vco; | ||
694 | |||
695 | msel = (bfin_read_PLL_CTL() >> 9) & 0x3F; | ||
696 | if (0 == msel) | ||
697 | msel = 64; | ||
698 | |||
699 | vco = CONFIG_CLKIN_HZ; | ||
700 | vco >>= (1 & bfin_read_PLL_CTL()); /* DF bit */ | ||
701 | vco = msel * vco; | ||
702 | return vco; | ||
703 | } | ||
704 | |||
705 | /*Get the Core clock*/ | ||
706 | u_long get_cclk(void) | ||
707 | { | ||
708 | u_long csel, ssel; | ||
709 | if (bfin_read_PLL_STAT() & 0x1) | ||
710 | return CONFIG_CLKIN_HZ; | ||
711 | |||
712 | ssel = bfin_read_PLL_DIV(); | ||
713 | csel = ((ssel >> 4) & 0x03); | ||
714 | ssel &= 0xf; | ||
715 | if (ssel && ssel < (1 << csel)) /* SCLK > CCLK */ | ||
716 | return get_vco() / ssel; | ||
717 | return get_vco() >> csel; | ||
718 | } | ||
719 | |||
720 | EXPORT_SYMBOL(get_cclk); | ||
721 | |||
722 | /* Get the System clock */ | ||
723 | u_long get_sclk(void) | ||
724 | { | ||
725 | u_long ssel; | ||
726 | |||
727 | if (bfin_read_PLL_STAT() & 0x1) | ||
728 | return CONFIG_CLKIN_HZ; | ||
729 | |||
730 | ssel = (bfin_read_PLL_DIV() & 0xf); | ||
731 | if (0 == ssel) { | ||
732 | printk(KERN_WARNING "Invalid System Clock\n"); | ||
733 | ssel = 1; | ||
734 | } | ||
735 | |||
736 | return get_vco() / ssel; | ||
737 | } | ||
738 | |||
739 | EXPORT_SYMBOL(get_sclk); | ||
740 | |||
741 | /* | ||
742 | * Get CPU information for use by the procfs. | ||
743 | */ | ||
744 | static int show_cpuinfo(struct seq_file *m, void *v) | ||
745 | { | ||
746 | char *cpu, *mmu, *fpu, *name; | ||
747 | uint32_t revid; | ||
748 | |||
749 | u_long cclk = 0, sclk = 0; | ||
750 | u_int dcache_size = 0, dsup_banks = 0; | ||
751 | |||
752 | cpu = CPU; | ||
753 | mmu = "none"; | ||
754 | fpu = "none"; | ||
755 | revid = bfin_revid(); | ||
756 | name = bfin_board_name; | ||
757 | |||
758 | cclk = get_cclk(); | ||
759 | sclk = get_sclk(); | ||
760 | |||
761 | seq_printf(m, "CPU:\t\tADSP-%s Rev. 0.%d\n" | ||
762 | "MMU:\t\t%s\n" | ||
763 | "FPU:\t\t%s\n" | ||
764 | "Core Clock:\t%9lu Hz\n" | ||
765 | "System Clock:\t%9lu Hz\n" | ||
766 | "BogoMips:\t%lu.%02lu\n" | ||
767 | "Calibration:\t%lu loops\n", | ||
768 | cpu, revid, mmu, fpu, | ||
769 | cclk, | ||
770 | sclk, | ||
771 | (loops_per_jiffy * HZ) / 500000, | ||
772 | ((loops_per_jiffy * HZ) / 5000) % 100, | ||
773 | (loops_per_jiffy * HZ)); | ||
774 | seq_printf(m, "Board Name:\t%s\n", name); | ||
775 | seq_printf(m, "Board Memory:\t%ld MB\n", physical_mem_end >> 20); | ||
776 | seq_printf(m, "Kernel Memory:\t%ld MB\n", (unsigned long)_ramend >> 20); | ||
777 | if (bfin_read_IMEM_CONTROL() & (ENICPLB | IMC)) | ||
778 | seq_printf(m, "I-CACHE:\tON\n"); | ||
779 | else | ||
780 | seq_printf(m, "I-CACHE:\tOFF\n"); | ||
781 | if ((bfin_read_DMEM_CONTROL()) & (ENDCPLB | DMC_ENABLE)) | ||
782 | seq_printf(m, "D-CACHE:\tON" | ||
783 | #if defined CONFIG_BLKFIN_WB | ||
784 | " (write-back)" | ||
785 | #elif defined CONFIG_BLKFIN_WT | ||
786 | " (write-through)" | ||
787 | #endif | ||
788 | "\n"); | ||
789 | else | ||
790 | seq_printf(m, "D-CACHE:\tOFF\n"); | ||
791 | |||
792 | |||
793 | switch(bfin_read_DMEM_CONTROL() & (1 << DMC0_P | 1 << DMC1_P)) { | ||
794 | case ACACHE_BSRAM: | ||
795 | seq_printf(m, "DBANK-A:\tCACHE\n" "DBANK-B:\tSRAM\n"); | ||
796 | dcache_size = 16; | ||
797 | dsup_banks = 1; | ||
798 | break; | ||
799 | case ACACHE_BCACHE: | ||
800 | seq_printf(m, "DBANK-A:\tCACHE\n" "DBANK-B:\tCACHE\n"); | ||
801 | dcache_size = 32; | ||
802 | dsup_banks = 2; | ||
803 | break; | ||
804 | case ASRAM_BSRAM: | ||
805 | seq_printf(m, "DBANK-A:\tSRAM\n" "DBANK-B:\tSRAM\n"); | ||
806 | dcache_size = 0; | ||
807 | dsup_banks = 0; | ||
808 | break; | ||
809 | default: | ||
810 | break; | ||
811 | } | ||
812 | |||
813 | |||
814 | seq_printf(m, "I-CACHE Size:\t%dKB\n", BLKFIN_ICACHESIZE / 1024); | ||
815 | seq_printf(m, "D-CACHE Size:\t%dKB\n", dcache_size); | ||
816 | seq_printf(m, "I-CACHE Setup:\t%d Sub-banks/%d Ways, %d Lines/Way\n", | ||
817 | BLKFIN_ISUBBANKS, BLKFIN_IWAYS, BLKFIN_ILINES); | ||
818 | seq_printf(m, | ||
819 | "D-CACHE Setup:\t%d Super-banks/%d Sub-banks/%d Ways, %d Lines/Way\n", | ||
820 | dsup_banks, BLKFIN_DSUBBANKS, BLKFIN_DWAYS, | ||
821 | BLKFIN_DLINES); | ||
822 | #ifdef CONFIG_BLKFIN_CACHE_LOCK | ||
823 | switch (read_iloc()) { | ||
824 | case WAY0_L: | ||
825 | seq_printf(m, "Way0 Locked-Down\n"); | ||
826 | break; | ||
827 | case WAY1_L: | ||
828 | seq_printf(m, "Way1 Locked-Down\n"); | ||
829 | break; | ||
830 | case WAY01_L: | ||
831 | seq_printf(m, "Way0,Way1 Locked-Down\n"); | ||
832 | break; | ||
833 | case WAY2_L: | ||
834 | seq_printf(m, "Way2 Locked-Down\n"); | ||
835 | break; | ||
836 | case WAY02_L: | ||
837 | seq_printf(m, "Way0,Way2 Locked-Down\n"); | ||
838 | break; | ||
839 | case WAY12_L: | ||
840 | seq_printf(m, "Way1,Way2 Locked-Down\n"); | ||
841 | break; | ||
842 | case WAY012_L: | ||
843 | seq_printf(m, "Way0,Way1 & Way2 Locked-Down\n"); | ||
844 | break; | ||
845 | case WAY3_L: | ||
846 | seq_printf(m, "Way3 Locked-Down\n"); | ||
847 | break; | ||
848 | case WAY03_L: | ||
849 | seq_printf(m, "Way0,Way3 Locked-Down\n"); | ||
850 | break; | ||
851 | case WAY13_L: | ||
852 | seq_printf(m, "Way1,Way3 Locked-Down\n"); | ||
853 | break; | ||
854 | case WAY013_L: | ||
855 | seq_printf(m, "Way 0,Way1,Way3 Locked-Down\n"); | ||
856 | break; | ||
857 | case WAY32_L: | ||
858 | seq_printf(m, "Way3,Way2 Locked-Down\n"); | ||
859 | break; | ||
860 | case WAY320_L: | ||
861 | seq_printf(m, "Way3,Way2,Way0 Locked-Down\n"); | ||
862 | break; | ||
863 | case WAY321_L: | ||
864 | seq_printf(m, "Way3,Way2,Way1 Locked-Down\n"); | ||
865 | break; | ||
866 | case WAYALL_L: | ||
867 | seq_printf(m, "All Ways are locked\n"); | ||
868 | break; | ||
869 | default: | ||
870 | seq_printf(m, "No Ways are locked\n"); | ||
871 | } | ||
872 | #endif | ||
873 | return 0; | ||
874 | } | ||
875 | |||
876 | static void *c_start(struct seq_file *m, loff_t *pos) | ||
877 | { | ||
878 | return *pos < NR_CPUS ? ((void *)0x12345678) : NULL; | ||
879 | } | ||
880 | |||
881 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||
882 | { | ||
883 | ++*pos; | ||
884 | return c_start(m, pos); | ||
885 | } | ||
886 | |||
887 | static void c_stop(struct seq_file *m, void *v) | ||
888 | { | ||
889 | } | ||
890 | |||
891 | struct seq_operations cpuinfo_op = { | ||
892 | .start = c_start, | ||
893 | .next = c_next, | ||
894 | .stop = c_stop, | ||
895 | .show = show_cpuinfo, | ||
896 | }; | ||
897 | |||
898 | void cmdline_init(unsigned long r0) | ||
899 | { | ||
900 | if (r0) | ||
901 | strncpy(command_line, (char *)r0, COMMAND_LINE_SIZE); | ||
902 | } | ||
diff --git a/arch/blackfin/kernel/signal.c b/arch/blackfin/kernel/signal.c new file mode 100644 index 000000000000..316e65c3439d --- /dev/null +++ b/arch/blackfin/kernel/signal.c | |||
@@ -0,0 +1,356 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/signal.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <linux/signal.h> | ||
31 | #include <linux/syscalls.h> | ||
32 | #include <linux/ptrace.h> | ||
33 | #include <linux/tty.h> | ||
34 | #include <linux/personality.h> | ||
35 | #include <linux/binfmts.h> | ||
36 | #include <linux/freezer.h> | ||
37 | |||
38 | #include <asm/uaccess.h> | ||
39 | #include <asm/cacheflush.h> | ||
40 | #include <asm/ucontext.h> | ||
41 | |||
42 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
43 | |||
44 | struct fdpic_func_descriptor { | ||
45 | unsigned long text; | ||
46 | unsigned long GOT; | ||
47 | }; | ||
48 | |||
49 | struct rt_sigframe { | ||
50 | int sig; | ||
51 | struct siginfo *pinfo; | ||
52 | void *puc; | ||
53 | char retcode[8]; | ||
54 | struct siginfo info; | ||
55 | struct ucontext uc; | ||
56 | }; | ||
57 | |||
58 | asmlinkage int sys_sigaltstack(const stack_t * uss, stack_t * uoss) | ||
59 | { | ||
60 | return do_sigaltstack(uss, uoss, rdusp()); | ||
61 | } | ||
62 | |||
63 | static inline int | ||
64 | rt_restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *pr0) | ||
65 | { | ||
66 | unsigned long usp = 0; | ||
67 | int err = 0; | ||
68 | |||
69 | #define RESTORE(x) err |= __get_user(regs->x, &sc->sc_##x) | ||
70 | |||
71 | /* restore passed registers */ | ||
72 | RESTORE(r0); RESTORE(r1); RESTORE(r2); RESTORE(r3); | ||
73 | RESTORE(r4); RESTORE(r5); RESTORE(r6); RESTORE(r7); | ||
74 | RESTORE(p0); RESTORE(p1); RESTORE(p2); RESTORE(p3); | ||
75 | RESTORE(p4); RESTORE(p5); | ||
76 | err |= __get_user(usp, &sc->sc_usp); | ||
77 | wrusp(usp); | ||
78 | RESTORE(a0w); RESTORE(a1w); | ||
79 | RESTORE(a0x); RESTORE(a1x); | ||
80 | RESTORE(astat); | ||
81 | RESTORE(rets); | ||
82 | RESTORE(pc); | ||
83 | RESTORE(retx); | ||
84 | RESTORE(fp); | ||
85 | RESTORE(i0); RESTORE(i1); RESTORE(i2); RESTORE(i3); | ||
86 | RESTORE(m0); RESTORE(m1); RESTORE(m2); RESTORE(m3); | ||
87 | RESTORE(l0); RESTORE(l1); RESTORE(l2); RESTORE(l3); | ||
88 | RESTORE(b0); RESTORE(b1); RESTORE(b2); RESTORE(b3); | ||
89 | RESTORE(lc0); RESTORE(lc1); | ||
90 | RESTORE(lt0); RESTORE(lt1); | ||
91 | RESTORE(lb0); RESTORE(lb1); | ||
92 | RESTORE(seqstat); | ||
93 | |||
94 | regs->orig_p0 = -1; /* disable syscall checks */ | ||
95 | |||
96 | *pr0 = regs->r0; | ||
97 | return err; | ||
98 | } | ||
99 | |||
100 | asmlinkage int do_rt_sigreturn(unsigned long __unused) | ||
101 | { | ||
102 | struct pt_regs *regs = (struct pt_regs *)__unused; | ||
103 | unsigned long usp = rdusp(); | ||
104 | struct rt_sigframe *frame = (struct rt_sigframe *)(usp); | ||
105 | sigset_t set; | ||
106 | int r0; | ||
107 | |||
108 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
109 | goto badframe; | ||
110 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
111 | goto badframe; | ||
112 | |||
113 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
114 | spin_lock_irq(¤t->sighand->siglock); | ||
115 | current->blocked = set; | ||
116 | recalc_sigpending(); | ||
117 | spin_unlock_irq(¤t->sighand->siglock); | ||
118 | |||
119 | if (rt_restore_sigcontext(regs, &frame->uc.uc_mcontext, &r0)) | ||
120 | goto badframe; | ||
121 | |||
122 | if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->usp) == -EFAULT) | ||
123 | goto badframe; | ||
124 | |||
125 | return r0; | ||
126 | |||
127 | badframe: | ||
128 | force_sig(SIGSEGV, current); | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static inline int rt_setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs) | ||
133 | { | ||
134 | int err = 0; | ||
135 | |||
136 | #define SETUP(x) err |= __put_user(regs->x, &sc->sc_##x) | ||
137 | |||
138 | SETUP(r0); SETUP(r1); SETUP(r2); SETUP(r3); | ||
139 | SETUP(r4); SETUP(r5); SETUP(r6); SETUP(r7); | ||
140 | SETUP(p0); SETUP(p1); SETUP(p2); SETUP(p3); | ||
141 | SETUP(p4); SETUP(p5); | ||
142 | err |= __put_user(rdusp(), &sc->sc_usp); | ||
143 | SETUP(a0w); SETUP(a1w); | ||
144 | SETUP(a0x); SETUP(a1x); | ||
145 | SETUP(astat); | ||
146 | SETUP(rets); | ||
147 | SETUP(pc); | ||
148 | SETUP(retx); | ||
149 | SETUP(fp); | ||
150 | SETUP(i0); SETUP(i1); SETUP(i2); SETUP(i3); | ||
151 | SETUP(m0); SETUP(m1); SETUP(m2); SETUP(m3); | ||
152 | SETUP(l0); SETUP(l1); SETUP(l2); SETUP(l3); | ||
153 | SETUP(b0); SETUP(b1); SETUP(b2); SETUP(b3); | ||
154 | SETUP(lc0); SETUP(lc1); | ||
155 | SETUP(lt0); SETUP(lt1); | ||
156 | SETUP(lb0); SETUP(lb1); | ||
157 | SETUP(seqstat); | ||
158 | |||
159 | return err; | ||
160 | } | ||
161 | |||
162 | static inline void push_cache(unsigned long vaddr, unsigned int len) | ||
163 | { | ||
164 | flush_icache_range(vaddr, vaddr + len); | ||
165 | } | ||
166 | |||
167 | static inline void *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, | ||
168 | size_t frame_size) | ||
169 | { | ||
170 | unsigned long usp; | ||
171 | |||
172 | /* Default to using normal stack. */ | ||
173 | usp = rdusp(); | ||
174 | |||
175 | /* This is the X/Open sanctioned signal stack switching. */ | ||
176 | if (ka->sa.sa_flags & SA_ONSTACK) { | ||
177 | if (!on_sig_stack(usp)) | ||
178 | usp = current->sas_ss_sp + current->sas_ss_size; | ||
179 | } | ||
180 | return (void *)((usp - frame_size) & -8UL); | ||
181 | } | ||
182 | |||
183 | static int | ||
184 | setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t * info, | ||
185 | sigset_t * set, struct pt_regs *regs) | ||
186 | { | ||
187 | struct rt_sigframe *frame; | ||
188 | int err = 0; | ||
189 | |||
190 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
191 | |||
192 | err |= __put_user((current_thread_info()->exec_domain | ||
193 | && current_thread_info()->exec_domain->signal_invmap | ||
194 | && sig < 32 | ||
195 | ? current_thread_info()->exec_domain-> | ||
196 | signal_invmap[sig] : sig), &frame->sig); | ||
197 | |||
198 | err |= __put_user(&frame->info, &frame->pinfo); | ||
199 | err |= __put_user(&frame->uc, &frame->puc); | ||
200 | err |= copy_siginfo_to_user(&frame->info, info); | ||
201 | |||
202 | /* Create the ucontext. */ | ||
203 | err |= __put_user(0, &frame->uc.uc_flags); | ||
204 | err |= __put_user(0, &frame->uc.uc_link); | ||
205 | err |= | ||
206 | __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | ||
207 | err |= __put_user(sas_ss_flags(rdusp()), &frame->uc.uc_stack.ss_flags); | ||
208 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
209 | err |= rt_setup_sigcontext(&frame->uc.uc_mcontext, regs); | ||
210 | err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
211 | |||
212 | /* Set up to return from userspace. */ | ||
213 | err |= __put_user(0x28, &(frame->retcode[0])); | ||
214 | err |= __put_user(0xe1, &(frame->retcode[1])); | ||
215 | err |= __put_user(0xad, &(frame->retcode[2])); | ||
216 | err |= __put_user(0x00, &(frame->retcode[3])); | ||
217 | err |= __put_user(0xa0, &(frame->retcode[4])); | ||
218 | err |= __put_user(0x00, &(frame->retcode[5])); | ||
219 | |||
220 | if (err) | ||
221 | goto give_sigsegv; | ||
222 | |||
223 | push_cache((unsigned long)&frame->retcode, sizeof(frame->retcode)); | ||
224 | |||
225 | /* Set up registers for signal handler */ | ||
226 | wrusp((unsigned long)frame); | ||
227 | if (get_personality & FDPIC_FUNCPTRS) { | ||
228 | struct fdpic_func_descriptor __user *funcptr = | ||
229 | (struct fdpic_func_descriptor *) ka->sa.sa_handler; | ||
230 | __get_user(regs->pc, &funcptr->text); | ||
231 | __get_user(regs->p3, &funcptr->GOT); | ||
232 | } else | ||
233 | regs->pc = (unsigned long)ka->sa.sa_handler; | ||
234 | regs->rets = (unsigned long)(frame->retcode); | ||
235 | |||
236 | regs->r0 = frame->sig; | ||
237 | regs->r1 = (unsigned long)(&frame->info); | ||
238 | regs->r2 = (unsigned long)(&frame->uc); | ||
239 | |||
240 | return 0; | ||
241 | |||
242 | give_sigsegv: | ||
243 | if (sig == SIGSEGV) | ||
244 | ka->sa.sa_handler = SIG_DFL; | ||
245 | force_sig(SIGSEGV, current); | ||
246 | return -EFAULT; | ||
247 | } | ||
248 | |||
249 | static inline void | ||
250 | handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) | ||
251 | { | ||
252 | switch (regs->r0) { | ||
253 | case -ERESTARTNOHAND: | ||
254 | if (!has_handler) | ||
255 | goto do_restart; | ||
256 | regs->r0 = -EINTR; | ||
257 | break; | ||
258 | |||
259 | case -ERESTARTSYS: | ||
260 | if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { | ||
261 | regs->r0 = -EINTR; | ||
262 | break; | ||
263 | } | ||
264 | /* fallthrough */ | ||
265 | case -ERESTARTNOINTR: | ||
266 | do_restart: | ||
267 | regs->p0 = regs->orig_p0; | ||
268 | regs->r0 = regs->orig_r0; | ||
269 | regs->pc -= 2; | ||
270 | break; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * OK, we're invoking a handler | ||
276 | */ | ||
277 | static int | ||
278 | handle_signal(int sig, siginfo_t *info, struct k_sigaction *ka, | ||
279 | sigset_t *oldset, struct pt_regs *regs) | ||
280 | { | ||
281 | int ret; | ||
282 | |||
283 | /* are we from a system call? to see pt_regs->orig_p0 */ | ||
284 | if (regs->orig_p0 >= 0) | ||
285 | /* If so, check system call restarting.. */ | ||
286 | handle_restart(regs, ka, 1); | ||
287 | |||
288 | /* set up the stack frame */ | ||
289 | ret = setup_rt_frame(sig, ka, info, oldset, regs); | ||
290 | |||
291 | if (ret == 0) { | ||
292 | spin_lock_irq(¤t->sighand->siglock); | ||
293 | sigorsets(¤t->blocked, ¤t->blocked, | ||
294 | &ka->sa.sa_mask); | ||
295 | if (!(ka->sa.sa_flags & SA_NODEFER)) | ||
296 | sigaddset(¤t->blocked, sig); | ||
297 | recalc_sigpending(); | ||
298 | spin_unlock_irq(¤t->sighand->siglock); | ||
299 | } | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
305 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
306 | * mistake. | ||
307 | * | ||
308 | * Note that we go through the signals twice: once to check the signals | ||
309 | * that the kernel can handle, and then we build all the user-level signal | ||
310 | * handling stack-frames in one go after that. | ||
311 | */ | ||
312 | asmlinkage void do_signal(struct pt_regs *regs) | ||
313 | { | ||
314 | siginfo_t info; | ||
315 | int signr; | ||
316 | struct k_sigaction ka; | ||
317 | sigset_t *oldset; | ||
318 | |||
319 | current->thread.esp0 = (unsigned long)regs; | ||
320 | |||
321 | if (try_to_freeze()) | ||
322 | goto no_signal; | ||
323 | |||
324 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
325 | oldset = ¤t->saved_sigmask; | ||
326 | else | ||
327 | oldset = ¤t->blocked; | ||
328 | |||
329 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
330 | if (signr > 0) { | ||
331 | /* Whee! Actually deliver the signal. */ | ||
332 | if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { | ||
333 | /* a signal was successfully delivered; the saved | ||
334 | * sigmask will have been stored in the signal frame, | ||
335 | * and will be restored by sigreturn, so we can simply | ||
336 | * clear the TIF_RESTORE_SIGMASK flag */ | ||
337 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
338 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
339 | } | ||
340 | |||
341 | return; | ||
342 | } | ||
343 | |||
344 | no_signal: | ||
345 | /* Did we come from a system call? */ | ||
346 | if (regs->orig_p0 >= 0) | ||
347 | /* Restart the system call - no handlers present */ | ||
348 | handle_restart(regs, NULL, 0); | ||
349 | |||
350 | /* if there's no signal to deliver, we just put the saved sigmask | ||
351 | * back */ | ||
352 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
353 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
354 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
355 | } | ||
356 | } | ||
diff --git a/arch/blackfin/kernel/sys_bfin.c b/arch/blackfin/kernel/sys_bfin.c new file mode 100644 index 000000000000..f436e6743f5a --- /dev/null +++ b/arch/blackfin/kernel/sys_bfin.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/sys_bfin.c | ||
3 | * Based on: | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: This file contains various random system calls that | ||
8 | * have a non-standard calling sequence on the Linux/bfin | ||
9 | * platform. | ||
10 | * | ||
11 | * Modified: | ||
12 | * Copyright 2004-2006 Analog Devices Inc. | ||
13 | * | ||
14 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License as published by | ||
18 | * the Free Software Foundation; either version 2 of the License, or | ||
19 | * (at your option) any later version. | ||
20 | * | ||
21 | * This program is distributed in the hope that it will be useful, | ||
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
24 | * GNU General Public License for more details. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License | ||
27 | * along with this program; if not, see the file COPYING, or write | ||
28 | * to the Free Software Foundation, Inc., | ||
29 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
30 | */ | ||
31 | |||
32 | #include <linux/smp_lock.h> | ||
33 | #include <linux/spinlock.h> | ||
34 | #include <linux/sem.h> | ||
35 | #include <linux/msg.h> | ||
36 | #include <linux/shm.h> | ||
37 | #include <linux/syscalls.h> | ||
38 | #include <linux/mman.h> | ||
39 | #include <linux/file.h> | ||
40 | |||
41 | #include <asm/cacheflush.h> | ||
42 | #include <asm/uaccess.h> | ||
43 | #include <asm/ipc.h> | ||
44 | #include <asm/dma.h> | ||
45 | #include <asm/unistd.h> | ||
46 | |||
47 | /* | ||
48 | * sys_pipe() is the normal C calling standard for creating | ||
49 | * a pipe. It's not the way unix traditionally does this, though. | ||
50 | */ | ||
51 | asmlinkage int sys_pipe(unsigned long *fildes) | ||
52 | { | ||
53 | int fd[2]; | ||
54 | int error; | ||
55 | |||
56 | error = do_pipe(fd); | ||
57 | if (!error) { | ||
58 | if (copy_to_user(fildes, fd, 2 * sizeof(int))) | ||
59 | error = -EFAULT; | ||
60 | } | ||
61 | return error; | ||
62 | } | ||
63 | |||
64 | /* common code for old and new mmaps */ | ||
65 | static inline long | ||
66 | do_mmap2(unsigned long addr, unsigned long len, | ||
67 | unsigned long prot, unsigned long flags, | ||
68 | unsigned long fd, unsigned long pgoff) | ||
69 | { | ||
70 | int error = -EBADF; | ||
71 | struct file *file = NULL; | ||
72 | |||
73 | flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); | ||
74 | if (!(flags & MAP_ANONYMOUS)) { | ||
75 | file = fget(fd); | ||
76 | if (!file) | ||
77 | goto out; | ||
78 | } | ||
79 | |||
80 | down_write(¤t->mm->mmap_sem); | ||
81 | error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); | ||
82 | up_write(¤t->mm->mmap_sem); | ||
83 | |||
84 | if (file) | ||
85 | fput(file); | ||
86 | out: | ||
87 | return error; | ||
88 | } | ||
89 | |||
90 | asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, | ||
91 | unsigned long prot, unsigned long flags, | ||
92 | unsigned long fd, unsigned long pgoff) | ||
93 | { | ||
94 | return do_mmap2(addr, len, prot, flags, fd, pgoff); | ||
95 | } | ||
96 | |||
97 | asmlinkage int sys_getpagesize(void) | ||
98 | { | ||
99 | return PAGE_SIZE; | ||
100 | } | ||
101 | |||
102 | asmlinkage void *sys_sram_alloc(size_t size, unsigned long flags) | ||
103 | { | ||
104 | return sram_alloc_with_lsl(size, flags); | ||
105 | } | ||
106 | |||
107 | asmlinkage int sys_sram_free(const void *addr) | ||
108 | { | ||
109 | return sram_free_with_lsl(addr); | ||
110 | } | ||
111 | |||
112 | asmlinkage void *sys_dma_memcpy(void *dest, const void *src, size_t len) | ||
113 | { | ||
114 | return safe_dma_memcpy(dest, src, len); | ||
115 | } | ||
diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c new file mode 100644 index 000000000000..f578176b6d92 --- /dev/null +++ b/arch/blackfin/kernel/time.c | |||
@@ -0,0 +1,326 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/time.c | ||
3 | * Based on: none - original work | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: This file contains the bfin-specific time handling details. | ||
8 | * Most of the stuff is located in the machine specific files. | ||
9 | * | ||
10 | * Modified: | ||
11 | * Copyright 2004-2006 Analog Devices Inc. | ||
12 | * | ||
13 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License as published by | ||
17 | * the Free Software Foundation; either version 2 of the License, or | ||
18 | * (at your option) any later version. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, see the file COPYING, or write | ||
27 | * to the Free Software Foundation, Inc., | ||
28 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
29 | */ | ||
30 | |||
31 | #include <linux/module.h> | ||
32 | #include <linux/profile.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/time.h> | ||
35 | #include <linux/irq.h> | ||
36 | |||
37 | #include <asm/blackfin.h> | ||
38 | |||
39 | /* This is an NTP setting */ | ||
40 | #define TICK_SIZE (tick_nsec / 1000) | ||
41 | |||
42 | static void time_sched_init(irqreturn_t(*timer_routine) | ||
43 | (int, void *)); | ||
44 | static unsigned long gettimeoffset(void); | ||
45 | static inline void do_leds(void); | ||
46 | |||
47 | #if (defined(CONFIG_BFIN_ALIVE_LED) || defined(CONFIG_BFIN_IDLE_LED)) | ||
48 | void __init init_leds(void) | ||
49 | { | ||
50 | unsigned int tmp = 0; | ||
51 | |||
52 | #if defined(CONFIG_BFIN_ALIVE_LED) | ||
53 | /* config pins as output. */ | ||
54 | tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_DPORT(); | ||
55 | SSYNC(); | ||
56 | bfin_write_CONFIG_BFIN_ALIVE_LED_DPORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN); | ||
57 | SSYNC(); | ||
58 | |||
59 | /* First set led be off */ | ||
60 | tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT(); | ||
61 | SSYNC(); | ||
62 | bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp | CONFIG_BFIN_ALIVE_LED_PIN); /* light off */ | ||
63 | SSYNC(); | ||
64 | #endif | ||
65 | |||
66 | #if defined(CONFIG_BFIN_IDLE_LED) | ||
67 | /* config pins as output. */ | ||
68 | tmp = bfin_read_CONFIG_BFIN_IDLE_LED_DPORT(); | ||
69 | SSYNC(); | ||
70 | bfin_write_CONFIG_BFIN_IDLE_LED_DPORT(tmp | CONFIG_BFIN_IDLE_LED_PIN); | ||
71 | SSYNC(); | ||
72 | |||
73 | /* First set led be off */ | ||
74 | tmp = bfin_read_CONFIG_BFIN_IDLE_LED_PORT(); | ||
75 | SSYNC(); | ||
76 | bfin_write_CONFIG_BFIN_IDLE_LED_PORT(tmp | CONFIG_BFIN_IDLE_LED_PIN); /* light off */ | ||
77 | SSYNC(); | ||
78 | #endif | ||
79 | } | ||
80 | #else | ||
81 | void __init init_leds(void) | ||
82 | { | ||
83 | } | ||
84 | #endif | ||
85 | |||
86 | #if defined(CONFIG_BFIN_ALIVE_LED) | ||
87 | static inline void do_leds(void) | ||
88 | { | ||
89 | static unsigned int count = 50; | ||
90 | static int flag = 0; | ||
91 | unsigned short tmp = 0; | ||
92 | |||
93 | if (--count == 0) { | ||
94 | count = 50; | ||
95 | flag = ~flag; | ||
96 | } | ||
97 | tmp = bfin_read_CONFIG_BFIN_ALIVE_LED_PORT(); | ||
98 | SSYNC(); | ||
99 | |||
100 | if (flag) | ||
101 | tmp &= ~CONFIG_BFIN_ALIVE_LED_PIN; /* light on */ | ||
102 | else | ||
103 | tmp |= CONFIG_BFIN_ALIVE_LED_PIN; /* light off */ | ||
104 | |||
105 | bfin_write_CONFIG_BFIN_ALIVE_LED_PORT(tmp); | ||
106 | SSYNC(); | ||
107 | |||
108 | } | ||
109 | #else | ||
110 | static inline void do_leds(void) | ||
111 | { | ||
112 | } | ||
113 | #endif | ||
114 | |||
115 | static struct irqaction bfin_timer_irq = { | ||
116 | .name = "BFIN Timer Tick", | ||
117 | .flags = IRQF_DISABLED | ||
118 | }; | ||
119 | |||
120 | /* | ||
121 | * The way that the Blackfin core timer works is: | ||
122 | * - CCLK is divided by a programmable 8-bit pre-scaler (TSCALE) | ||
123 | * - Every time TSCALE ticks, a 32bit is counted down (TCOUNT) | ||
124 | * | ||
125 | * If you take the fastest clock (1ns, or 1GHz to make the math work easier) | ||
126 | * 10ms is 10,000,000 clock ticks, which fits easy into a 32-bit counter | ||
127 | * (32 bit counter is 4,294,967,296ns or 4.2 seconds) so, we don't need | ||
128 | * to use TSCALE, and program it to zero (which is pass CCLK through). | ||
129 | * If you feel like using it, try to keep HZ * TIMESCALE to some | ||
130 | * value that divides easy (like power of 2). | ||
131 | */ | ||
132 | |||
133 | #define TIME_SCALE 1 | ||
134 | |||
135 | static void | ||
136 | time_sched_init(irqreturn_t(*timer_routine) (int, void *)) | ||
137 | { | ||
138 | u32 tcount; | ||
139 | |||
140 | /* power up the timer, but don't enable it just yet */ | ||
141 | bfin_write_TCNTL(1); | ||
142 | CSYNC(); | ||
143 | |||
144 | /* | ||
145 | * the TSCALE prescaler counter. | ||
146 | */ | ||
147 | bfin_write_TSCALE((TIME_SCALE - 1)); | ||
148 | |||
149 | tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1); | ||
150 | bfin_write_TPERIOD(tcount); | ||
151 | bfin_write_TCOUNT(tcount); | ||
152 | |||
153 | /* now enable the timer */ | ||
154 | CSYNC(); | ||
155 | |||
156 | bfin_write_TCNTL(7); | ||
157 | |||
158 | bfin_timer_irq.handler = (irq_handler_t)timer_routine; | ||
159 | /* call setup_irq instead of request_irq because request_irq calls | ||
160 | * kmalloc which has not been initialized yet | ||
161 | */ | ||
162 | setup_irq(IRQ_CORETMR, &bfin_timer_irq); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Should return useconds since last timer tick | ||
167 | */ | ||
168 | static unsigned long gettimeoffset(void) | ||
169 | { | ||
170 | unsigned long offset; | ||
171 | unsigned long clocks_per_jiffy; | ||
172 | |||
173 | clocks_per_jiffy = bfin_read_TPERIOD(); | ||
174 | offset = | ||
175 | (clocks_per_jiffy - | ||
176 | bfin_read_TCOUNT()) / (((clocks_per_jiffy + 1) * HZ) / | ||
177 | USEC_PER_SEC); | ||
178 | |||
179 | /* Check if we just wrapped the counters and maybe missed a tick */ | ||
180 | if ((bfin_read_ILAT() & (1 << IRQ_CORETMR)) | ||
181 | && (offset < (100000 / HZ / 2))) | ||
182 | offset += (USEC_PER_SEC / HZ); | ||
183 | |||
184 | return offset; | ||
185 | } | ||
186 | |||
187 | static inline int set_rtc_mmss(unsigned long nowtime) | ||
188 | { | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * timer_interrupt() needs to keep up the real-time clock, | ||
194 | * as well as call the "do_timer()" routine every clocktick | ||
195 | */ | ||
196 | #ifdef CONFIG_CORE_TIMER_IRQ_L1 | ||
197 | irqreturn_t timer_interrupt(int irq, void *dummy)__attribute__((l1_text)); | ||
198 | #endif | ||
199 | |||
200 | irqreturn_t timer_interrupt(int irq, void *dummy) | ||
201 | { | ||
202 | /* last time the cmos clock got updated */ | ||
203 | static long last_rtc_update = 0; | ||
204 | |||
205 | write_seqlock(&xtime_lock); | ||
206 | |||
207 | do_timer(1); | ||
208 | do_leds(); | ||
209 | |||
210 | #ifndef CONFIG_SMP | ||
211 | update_process_times(user_mode(get_irq_regs())); | ||
212 | #endif | ||
213 | profile_tick(CPU_PROFILING); | ||
214 | |||
215 | /* | ||
216 | * If we have an externally synchronized Linux clock, then update | ||
217 | * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be | ||
218 | * called as close as possible to 500 ms before the new second starts. | ||
219 | */ | ||
220 | |||
221 | if (ntp_synced() && | ||
222 | xtime.tv_sec > last_rtc_update + 660 && | ||
223 | (xtime.tv_nsec / NSEC_PER_USEC) >= | ||
224 | 500000 - ((unsigned)TICK_SIZE) / 2 | ||
225 | && (xtime.tv_nsec / NSEC_PER_USEC) <= | ||
226 | 500000 + ((unsigned)TICK_SIZE) / 2) { | ||
227 | if (set_rtc_mmss(xtime.tv_sec) == 0) | ||
228 | last_rtc_update = xtime.tv_sec; | ||
229 | else | ||
230 | /* Do it again in 60s. */ | ||
231 | last_rtc_update = xtime.tv_sec - 600; | ||
232 | } | ||
233 | write_sequnlock(&xtime_lock); | ||
234 | return IRQ_HANDLED; | ||
235 | } | ||
236 | |||
237 | void __init time_init(void) | ||
238 | { | ||
239 | time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */ | ||
240 | |||
241 | #ifdef CONFIG_RTC_DRV_BFIN | ||
242 | /* [#2663] hack to filter junk RTC values that would cause | ||
243 | * userspace to have to deal with time values greater than | ||
244 | * 2^31 seconds (which uClibc cannot cope with yet) | ||
245 | */ | ||
246 | if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) { | ||
247 | printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n"); | ||
248 | bfin_write_RTC_STAT(0); | ||
249 | } | ||
250 | #endif | ||
251 | |||
252 | /* Initialize xtime. From now on, xtime is updated with timer interrupts */ | ||
253 | xtime.tv_sec = secs_since_1970; | ||
254 | xtime.tv_nsec = 0; | ||
255 | |||
256 | wall_to_monotonic.tv_sec = -xtime.tv_sec; | ||
257 | |||
258 | time_sched_init(timer_interrupt); | ||
259 | } | ||
260 | |||
261 | #ifndef CONFIG_GENERIC_TIME | ||
262 | void do_gettimeofday(struct timeval *tv) | ||
263 | { | ||
264 | unsigned long flags; | ||
265 | unsigned long seq; | ||
266 | unsigned long usec, sec; | ||
267 | |||
268 | do { | ||
269 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | ||
270 | usec = gettimeoffset(); | ||
271 | sec = xtime.tv_sec; | ||
272 | usec += (xtime.tv_nsec / NSEC_PER_USEC); | ||
273 | } | ||
274 | while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | ||
275 | |||
276 | while (usec >= USEC_PER_SEC) { | ||
277 | usec -= USEC_PER_SEC; | ||
278 | sec++; | ||
279 | } | ||
280 | |||
281 | tv->tv_sec = sec; | ||
282 | tv->tv_usec = usec; | ||
283 | } | ||
284 | EXPORT_SYMBOL(do_gettimeofday); | ||
285 | |||
286 | int do_settimeofday(struct timespec *tv) | ||
287 | { | ||
288 | time_t wtm_sec, sec = tv->tv_sec; | ||
289 | long wtm_nsec, nsec = tv->tv_nsec; | ||
290 | |||
291 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | ||
292 | return -EINVAL; | ||
293 | |||
294 | write_seqlock_irq(&xtime_lock); | ||
295 | /* | ||
296 | * This is revolting. We need to set the xtime.tv_usec | ||
297 | * correctly. However, the value in this location is | ||
298 | * is value at the last tick. | ||
299 | * Discover what correction gettimeofday | ||
300 | * would have done, and then undo it! | ||
301 | */ | ||
302 | nsec -= (gettimeoffset() * NSEC_PER_USEC); | ||
303 | |||
304 | wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | ||
305 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | ||
306 | |||
307 | set_normalized_timespec(&xtime, sec, nsec); | ||
308 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | ||
309 | |||
310 | ntp_clear(); | ||
311 | |||
312 | write_sequnlock_irq(&xtime_lock); | ||
313 | clock_was_set(); | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | EXPORT_SYMBOL(do_settimeofday); | ||
318 | #endif /* !CONFIG_GENERIC_TIME */ | ||
319 | |||
320 | /* | ||
321 | * Scheduler clock - returns current time in nanosec units. | ||
322 | */ | ||
323 | unsigned long long sched_clock(void) | ||
324 | { | ||
325 | return (unsigned long long)jiffies *(NSEC_PER_SEC / HZ); | ||
326 | } | ||
diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c new file mode 100644 index 000000000000..9556b73de808 --- /dev/null +++ b/arch/blackfin/kernel/traps.c | |||
@@ -0,0 +1,649 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/traps.c | ||
3 | * Based on: | ||
4 | * Author: Hamish Macdonald | ||
5 | * | ||
6 | * Created: | ||
7 | * Description: uses S/W interrupt 15 for the system calls | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #include <asm/uaccess.h> | ||
31 | #include <asm/traps.h> | ||
32 | #include <asm/cacheflush.h> | ||
33 | #include <asm/blackfin.h> | ||
34 | #include <asm/uaccess.h> | ||
35 | #include <asm/irq_handler.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/module.h> | ||
38 | #include <linux/kallsyms.h> | ||
39 | |||
40 | #ifdef CONFIG_KGDB | ||
41 | # include <linux/debugger.h> | ||
42 | # include <linux/kgdb.h> | ||
43 | #endif | ||
44 | |||
45 | /* Initiate the event table handler */ | ||
46 | void __init trap_init(void) | ||
47 | { | ||
48 | CSYNC(); | ||
49 | bfin_write_EVT3(trap); | ||
50 | CSYNC(); | ||
51 | } | ||
52 | |||
53 | asmlinkage void trap_c(struct pt_regs *fp); | ||
54 | |||
55 | int kstack_depth_to_print = 48; | ||
56 | |||
57 | static int printk_address(unsigned long address) | ||
58 | { | ||
59 | struct vm_list_struct *vml; | ||
60 | struct task_struct *p; | ||
61 | struct mm_struct *mm; | ||
62 | |||
63 | #ifdef CONFIG_KALLSYMS | ||
64 | unsigned long offset = 0, symsize; | ||
65 | const char *symname; | ||
66 | char *modname; | ||
67 | char *delim = ":"; | ||
68 | char namebuf[128]; | ||
69 | |||
70 | /* look up the address and see if we are in kernel space */ | ||
71 | symname = kallsyms_lookup(address, &symsize, &offset, &modname, namebuf); | ||
72 | |||
73 | if (symname) { | ||
74 | /* yeah! kernel space! */ | ||
75 | if (!modname) | ||
76 | modname = delim = ""; | ||
77 | return printk("<0x%p> { %s%s%s%s + 0x%lx }", | ||
78 | (void*)address, delim, modname, delim, symname, | ||
79 | (unsigned long)offset); | ||
80 | |||
81 | } | ||
82 | #endif | ||
83 | |||
84 | /* looks like we're off in user-land, so let's walk all the | ||
85 | * mappings of all our processes and see if we can't be a whee | ||
86 | * bit more specific | ||
87 | */ | ||
88 | write_lock_irq(&tasklist_lock); | ||
89 | for_each_process(p) { | ||
90 | mm = get_task_mm(p); | ||
91 | if (!mm) | ||
92 | continue; | ||
93 | |||
94 | vml = mm->context.vmlist; | ||
95 | while (vml) { | ||
96 | struct vm_area_struct *vma = vml->vma; | ||
97 | |||
98 | if (address >= vma->vm_start && address < vma->vm_end) { | ||
99 | char *name = p->comm; | ||
100 | struct file *file = vma->vm_file; | ||
101 | if (file) { | ||
102 | char _tmpbuf[256]; | ||
103 | name = d_path(file->f_dentry, | ||
104 | file->f_vfsmnt, | ||
105 | _tmpbuf, | ||
106 | sizeof(_tmpbuf)); | ||
107 | } | ||
108 | |||
109 | write_unlock_irq(&tasklist_lock); | ||
110 | return printk("<0x%p> [ %s + 0x%lx ]", | ||
111 | (void*)address, name, | ||
112 | (unsigned long) | ||
113 | ((address - vma->vm_start) + | ||
114 | (vma->vm_pgoff << PAGE_SHIFT))); | ||
115 | } | ||
116 | |||
117 | vml = vml->next; | ||
118 | } | ||
119 | } | ||
120 | write_unlock_irq(&tasklist_lock); | ||
121 | |||
122 | /* we were unable to find this address anywhere */ | ||
123 | return printk("[<0x%p>]", (void*)address); | ||
124 | } | ||
125 | |||
126 | #define trace_buffer_save(x) \ | ||
127 | do { \ | ||
128 | (x) = bfin_read_TBUFCTL(); \ | ||
129 | bfin_write_TBUFCTL((x) & ~TBUFEN); \ | ||
130 | } while (0) | ||
131 | #define trace_buffer_restore(x) \ | ||
132 | do { \ | ||
133 | bfin_write_TBUFCTL((x)); \ | ||
134 | } while (0) | ||
135 | |||
136 | asmlinkage void trap_c(struct pt_regs *fp) | ||
137 | { | ||
138 | int j, sig = 0; | ||
139 | siginfo_t info; | ||
140 | unsigned long trapnr = fp->seqstat & SEQSTAT_EXCAUSE; | ||
141 | |||
142 | #ifdef CONFIG_KGDB | ||
143 | # define CHK_DEBUGGER_TRAP() do { CHK_DEBUGGER(trapnr, sig, info.si_code, fp,); } while (0) | ||
144 | # define CHK_DEBUGGER_TRAP_MAYBE() do { if (kgdb_connected) CHK_DEBUGGER_TRAP(); } while (0) | ||
145 | #else | ||
146 | # define CHK_DEBUGGER_TRAP() do { } while (0) | ||
147 | # define CHK_DEBUGGER_TRAP_MAYBE() do { } while (0) | ||
148 | #endif | ||
149 | |||
150 | trace_buffer_save(j); | ||
151 | |||
152 | /* trap_c() will be called for exceptions. During exceptions | ||
153 | * processing, the pc value should be set with retx value. | ||
154 | * With this change we can cleanup some code in signal.c- TODO | ||
155 | */ | ||
156 | fp->orig_pc = fp->retx; | ||
157 | /* printk("exception: 0x%x, ipend=%x, reti=%x, retx=%x\n", | ||
158 | trapnr, fp->ipend, fp->pc, fp->retx); */ | ||
159 | |||
160 | /* send the appropriate signal to the user program */ | ||
161 | switch (trapnr) { | ||
162 | |||
163 | /* This table works in conjuction with the one in ./mach-common/entry.S | ||
164 | * Some exceptions are handled there (in assembly, in exception space) | ||
165 | * Some are handled here, (in C, in interrupt space) | ||
166 | * Some, like CPLB, are handled in both, where the normal path is | ||
167 | * handled in assembly/exception space, and the error path is handled | ||
168 | * here | ||
169 | */ | ||
170 | |||
171 | /* 0x00 - Linux Syscall, getting here is an error */ | ||
172 | /* 0x01 - userspace gdb breakpoint, handled here */ | ||
173 | case VEC_EXCPT01: | ||
174 | info.si_code = TRAP_ILLTRAP; | ||
175 | sig = SIGTRAP; | ||
176 | CHK_DEBUGGER_TRAP_MAYBE(); | ||
177 | /* Check if this is a breakpoint in kernel space */ | ||
178 | if (fp->ipend & 0xffc0) | ||
179 | return; | ||
180 | else | ||
181 | break; | ||
182 | #ifdef CONFIG_KGDB | ||
183 | case VEC_EXCPT02 : /* gdb connection */ | ||
184 | info.si_code = TRAP_ILLTRAP; | ||
185 | sig = SIGTRAP; | ||
186 | CHK_DEBUGGER_TRAP(); | ||
187 | return; | ||
188 | #else | ||
189 | /* 0x02 - User Defined, Caught by default */ | ||
190 | #endif | ||
191 | /* 0x03 - Atomic test and set */ | ||
192 | case VEC_EXCPT03: | ||
193 | info.si_code = SEGV_STACKFLOW; | ||
194 | sig = SIGSEGV; | ||
195 | printk(KERN_EMERG EXC_0x03); | ||
196 | CHK_DEBUGGER_TRAP(); | ||
197 | break; | ||
198 | /* 0x04 - spinlock - handled by _ex_spinlock, | ||
199 | getting here is an error */ | ||
200 | /* 0x05 - User Defined, Caught by default */ | ||
201 | /* 0x06 - User Defined, Caught by default */ | ||
202 | /* 0x07 - User Defined, Caught by default */ | ||
203 | /* 0x08 - User Defined, Caught by default */ | ||
204 | /* 0x09 - User Defined, Caught by default */ | ||
205 | /* 0x0A - User Defined, Caught by default */ | ||
206 | /* 0x0B - User Defined, Caught by default */ | ||
207 | /* 0x0C - User Defined, Caught by default */ | ||
208 | /* 0x0D - User Defined, Caught by default */ | ||
209 | /* 0x0E - User Defined, Caught by default */ | ||
210 | /* 0x0F - User Defined, Caught by default */ | ||
211 | /* 0x10 HW Single step, handled here */ | ||
212 | case VEC_STEP: | ||
213 | info.si_code = TRAP_STEP; | ||
214 | sig = SIGTRAP; | ||
215 | CHK_DEBUGGER_TRAP_MAYBE(); | ||
216 | /* Check if this is a single step in kernel space */ | ||
217 | if (fp->ipend & 0xffc0) | ||
218 | return; | ||
219 | else | ||
220 | break; | ||
221 | /* 0x11 - Trace Buffer Full, handled here */ | ||
222 | case VEC_OVFLOW: | ||
223 | info.si_code = TRAP_TRACEFLOW; | ||
224 | sig = SIGTRAP; | ||
225 | printk(KERN_EMERG EXC_0x11); | ||
226 | CHK_DEBUGGER_TRAP(); | ||
227 | break; | ||
228 | /* 0x12 - Reserved, Caught by default */ | ||
229 | /* 0x13 - Reserved, Caught by default */ | ||
230 | /* 0x14 - Reserved, Caught by default */ | ||
231 | /* 0x15 - Reserved, Caught by default */ | ||
232 | /* 0x16 - Reserved, Caught by default */ | ||
233 | /* 0x17 - Reserved, Caught by default */ | ||
234 | /* 0x18 - Reserved, Caught by default */ | ||
235 | /* 0x19 - Reserved, Caught by default */ | ||
236 | /* 0x1A - Reserved, Caught by default */ | ||
237 | /* 0x1B - Reserved, Caught by default */ | ||
238 | /* 0x1C - Reserved, Caught by default */ | ||
239 | /* 0x1D - Reserved, Caught by default */ | ||
240 | /* 0x1E - Reserved, Caught by default */ | ||
241 | /* 0x1F - Reserved, Caught by default */ | ||
242 | /* 0x20 - Reserved, Caught by default */ | ||
243 | /* 0x21 - Undefined Instruction, handled here */ | ||
244 | case VEC_UNDEF_I: | ||
245 | info.si_code = ILL_ILLOPC; | ||
246 | sig = SIGILL; | ||
247 | printk(KERN_EMERG EXC_0x21); | ||
248 | CHK_DEBUGGER_TRAP(); | ||
249 | break; | ||
250 | /* 0x22 - Illegal Instruction Combination, handled here */ | ||
251 | case VEC_ILGAL_I: | ||
252 | info.si_code = ILL_ILLPARAOP; | ||
253 | sig = SIGILL; | ||
254 | printk(KERN_EMERG EXC_0x22); | ||
255 | CHK_DEBUGGER_TRAP(); | ||
256 | break; | ||
257 | /* 0x23 - Data CPLB Protection Violation, | ||
258 | normal case is handled in _cplb_hdr */ | ||
259 | case VEC_CPLB_VL: | ||
260 | info.si_code = ILL_CPLB_VI; | ||
261 | sig = SIGILL; | ||
262 | printk(KERN_EMERG EXC_0x23); | ||
263 | CHK_DEBUGGER_TRAP(); | ||
264 | break; | ||
265 | /* 0x24 - Data access misaligned, handled here */ | ||
266 | case VEC_MISALI_D: | ||
267 | info.si_code = BUS_ADRALN; | ||
268 | sig = SIGBUS; | ||
269 | printk(KERN_EMERG EXC_0x24); | ||
270 | CHK_DEBUGGER_TRAP(); | ||
271 | break; | ||
272 | /* 0x25 - Unrecoverable Event, handled here */ | ||
273 | case VEC_UNCOV: | ||
274 | info.si_code = ILL_ILLEXCPT; | ||
275 | sig = SIGILL; | ||
276 | printk(KERN_EMERG EXC_0x25); | ||
277 | CHK_DEBUGGER_TRAP(); | ||
278 | break; | ||
279 | /* 0x26 - Data CPLB Miss, normal case is handled in _cplb_hdr, | ||
280 | error case is handled here */ | ||
281 | case VEC_CPLB_M: | ||
282 | info.si_code = BUS_ADRALN; | ||
283 | sig = SIGBUS; | ||
284 | printk(KERN_EMERG EXC_0x26); | ||
285 | CHK_DEBUGGER_TRAP(); | ||
286 | break; | ||
287 | /* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero, handled here */ | ||
288 | case VEC_CPLB_MHIT: | ||
289 | info.si_code = ILL_CPLB_MULHIT; | ||
290 | #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO | ||
291 | sig = SIGSEGV; | ||
292 | printk(KERN_EMERG "\n\nNULL pointer access (probably)\n"); | ||
293 | #else | ||
294 | sig = SIGILL; | ||
295 | printk(KERN_EMERG EXC_0x27); | ||
296 | #endif | ||
297 | CHK_DEBUGGER_TRAP(); | ||
298 | break; | ||
299 | /* 0x28 - Emulation Watchpoint, handled here */ | ||
300 | case VEC_WATCH: | ||
301 | info.si_code = TRAP_WATCHPT; | ||
302 | sig = SIGTRAP; | ||
303 | pr_debug(EXC_0x28); | ||
304 | CHK_DEBUGGER_TRAP_MAYBE(); | ||
305 | /* Check if this is a watchpoint in kernel space */ | ||
306 | if (fp->ipend & 0xffc0) | ||
307 | return; | ||
308 | else | ||
309 | break; | ||
310 | #ifdef CONFIG_BF535 | ||
311 | /* 0x29 - Instruction fetch access error (535 only) */ | ||
312 | case VEC_ISTRU_VL: /* ADSP-BF535 only (MH) */ | ||
313 | info.si_code = BUS_OPFETCH; | ||
314 | sig = SIGBUS; | ||
315 | printk(KERN_EMERG "BF535: VEC_ISTRU_VL\n"); | ||
316 | CHK_DEBUGGER_TRAP(); | ||
317 | break; | ||
318 | #else | ||
319 | /* 0x29 - Reserved, Caught by default */ | ||
320 | #endif | ||
321 | /* 0x2A - Instruction fetch misaligned, handled here */ | ||
322 | case VEC_MISALI_I: | ||
323 | info.si_code = BUS_ADRALN; | ||
324 | sig = SIGBUS; | ||
325 | printk(KERN_EMERG EXC_0x2A); | ||
326 | CHK_DEBUGGER_TRAP(); | ||
327 | break; | ||
328 | /* 0x2B - Instruction CPLB protection Violation, | ||
329 | handled in _cplb_hdr */ | ||
330 | case VEC_CPLB_I_VL: | ||
331 | info.si_code = ILL_CPLB_VI; | ||
332 | sig = SIGILL; | ||
333 | printk(KERN_EMERG EXC_0x2B); | ||
334 | CHK_DEBUGGER_TRAP(); | ||
335 | break; | ||
336 | /* 0x2C - Instruction CPLB miss, handled in _cplb_hdr */ | ||
337 | case VEC_CPLB_I_M: | ||
338 | info.si_code = ILL_CPLB_MISS; | ||
339 | sig = SIGBUS; | ||
340 | printk(KERN_EMERG EXC_0x2C); | ||
341 | CHK_DEBUGGER_TRAP(); | ||
342 | break; | ||
343 | /* 0x2D - Instruction CPLB Multiple Hits, handled here */ | ||
344 | case VEC_CPLB_I_MHIT: | ||
345 | info.si_code = ILL_CPLB_MULHIT; | ||
346 | #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO | ||
347 | sig = SIGSEGV; | ||
348 | printk(KERN_EMERG "\n\nJump to address 0 - 0x0fff\n"); | ||
349 | #else | ||
350 | sig = SIGILL; | ||
351 | printk(KERN_EMERG EXC_0x2D); | ||
352 | #endif | ||
353 | CHK_DEBUGGER_TRAP(); | ||
354 | break; | ||
355 | /* 0x2E - Illegal use of Supervisor Resource, handled here */ | ||
356 | case VEC_ILL_RES: | ||
357 | info.si_code = ILL_PRVOPC; | ||
358 | sig = SIGILL; | ||
359 | printk(KERN_EMERG EXC_0x2E); | ||
360 | CHK_DEBUGGER_TRAP(); | ||
361 | break; | ||
362 | /* 0x2F - Reserved, Caught by default */ | ||
363 | /* 0x30 - Reserved, Caught by default */ | ||
364 | /* 0x31 - Reserved, Caught by default */ | ||
365 | /* 0x32 - Reserved, Caught by default */ | ||
366 | /* 0x33 - Reserved, Caught by default */ | ||
367 | /* 0x34 - Reserved, Caught by default */ | ||
368 | /* 0x35 - Reserved, Caught by default */ | ||
369 | /* 0x36 - Reserved, Caught by default */ | ||
370 | /* 0x37 - Reserved, Caught by default */ | ||
371 | /* 0x38 - Reserved, Caught by default */ | ||
372 | /* 0x39 - Reserved, Caught by default */ | ||
373 | /* 0x3A - Reserved, Caught by default */ | ||
374 | /* 0x3B - Reserved, Caught by default */ | ||
375 | /* 0x3C - Reserved, Caught by default */ | ||
376 | /* 0x3D - Reserved, Caught by default */ | ||
377 | /* 0x3E - Reserved, Caught by default */ | ||
378 | /* 0x3F - Reserved, Caught by default */ | ||
379 | default: | ||
380 | info.si_code = TRAP_ILLTRAP; | ||
381 | sig = SIGTRAP; | ||
382 | printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n", | ||
383 | (fp->seqstat & SEQSTAT_EXCAUSE)); | ||
384 | CHK_DEBUGGER_TRAP(); | ||
385 | break; | ||
386 | } | ||
387 | |||
388 | info.si_signo = sig; | ||
389 | info.si_errno = 0; | ||
390 | info.si_addr = (void *)fp->pc; | ||
391 | force_sig_info(sig, &info, current); | ||
392 | if (sig != 0 && sig != SIGTRAP) { | ||
393 | unsigned long stack; | ||
394 | dump_bfin_regs(fp, (void *)fp->retx); | ||
395 | dump_bfin_trace_buffer(); | ||
396 | show_stack(current, &stack); | ||
397 | if (current->mm == NULL) | ||
398 | panic("Kernel exception"); | ||
399 | } | ||
400 | |||
401 | /* if the address that we are about to return to is not valid, set it | ||
402 | * to a valid address, if we have a current application or panic | ||
403 | */ | ||
404 | if (!(fp->pc <= physical_mem_end | ||
405 | #if L1_CODE_LENGTH != 0 | ||
406 | || (fp->pc >= L1_CODE_START && | ||
407 | fp->pc <= (L1_CODE_START + L1_CODE_LENGTH)) | ||
408 | #endif | ||
409 | )) { | ||
410 | if (current->mm) { | ||
411 | fp->pc = current->mm->start_code; | ||
412 | } else { | ||
413 | printk(KERN_EMERG "I can't return to memory that doesn't exist - bad things happen\n"); | ||
414 | panic("Help - I've fallen and can't get up\n"); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | trace_buffer_restore(j); | ||
419 | return; | ||
420 | } | ||
421 | |||
422 | /* Typical exception handling routines */ | ||
423 | |||
424 | void dump_bfin_trace_buffer(void) | ||
425 | { | ||
426 | int tflags; | ||
427 | trace_buffer_save(tflags); | ||
428 | |||
429 | if (likely(bfin_read_TBUFSTAT() & TBUFCNT)) { | ||
430 | int i; | ||
431 | printk(KERN_EMERG "Hardware Trace:\n"); | ||
432 | for (i = 0; bfin_read_TBUFSTAT() & TBUFCNT; i++) { | ||
433 | printk(KERN_EMERG "%2i Target : ", i); | ||
434 | printk_address((unsigned long)bfin_read_TBUF()); | ||
435 | printk("\n" KERN_EMERG " Source : "); | ||
436 | printk_address((unsigned long)bfin_read_TBUF()); | ||
437 | printk("\n"); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | trace_buffer_restore(tflags); | ||
442 | } | ||
443 | EXPORT_SYMBOL(dump_bfin_trace_buffer); | ||
444 | |||
445 | static void show_trace(struct task_struct *tsk, unsigned long *sp) | ||
446 | { | ||
447 | unsigned long addr; | ||
448 | |||
449 | printk("\nCall Trace:"); | ||
450 | #ifdef CONFIG_KALLSYMS | ||
451 | printk("\n"); | ||
452 | #endif | ||
453 | |||
454 | while (!kstack_end(sp)) { | ||
455 | addr = *sp++; | ||
456 | /* | ||
457 | * If the address is either in the text segment of the | ||
458 | * kernel, or in the region which contains vmalloc'ed | ||
459 | * memory, it *may* be the address of a calling | ||
460 | * routine; if so, print it so that someone tracing | ||
461 | * down the cause of the crash will be able to figure | ||
462 | * out the call path that was taken. | ||
463 | */ | ||
464 | if (kernel_text_address(addr)) | ||
465 | print_ip_sym(addr); | ||
466 | } | ||
467 | |||
468 | printk("\n"); | ||
469 | } | ||
470 | |||
471 | void show_stack(struct task_struct *task, unsigned long *stack) | ||
472 | { | ||
473 | unsigned long *endstack, addr; | ||
474 | int i; | ||
475 | |||
476 | /* Cannot call dump_bfin_trace_buffer() here as show_stack() is | ||
477 | * called externally in some places in the kernel. | ||
478 | */ | ||
479 | |||
480 | if (!stack) { | ||
481 | if (task) | ||
482 | stack = (unsigned long *)task->thread.ksp; | ||
483 | else | ||
484 | stack = (unsigned long *)&stack; | ||
485 | } | ||
486 | |||
487 | addr = (unsigned long)stack; | ||
488 | endstack = (unsigned long *)PAGE_ALIGN(addr); | ||
489 | |||
490 | printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack); | ||
491 | for (i = 0; i < kstack_depth_to_print; i++) { | ||
492 | if (stack + 1 > endstack) | ||
493 | break; | ||
494 | if (i % 8 == 0) | ||
495 | printk("\n" KERN_EMERG " "); | ||
496 | printk(" %08lx", *stack++); | ||
497 | } | ||
498 | |||
499 | show_trace(task, stack); | ||
500 | } | ||
501 | |||
502 | void dump_stack(void) | ||
503 | { | ||
504 | unsigned long stack; | ||
505 | int tflags; | ||
506 | trace_buffer_save(tflags); | ||
507 | dump_bfin_trace_buffer(); | ||
508 | show_stack(current, &stack); | ||
509 | trace_buffer_restore(tflags); | ||
510 | } | ||
511 | |||
512 | EXPORT_SYMBOL(dump_stack); | ||
513 | |||
514 | void dump_bfin_regs(struct pt_regs *fp, void *retaddr) | ||
515 | { | ||
516 | if (current->pid) { | ||
517 | printk("\nCURRENT PROCESS:\n\n"); | ||
518 | printk("COMM=%s PID=%d\n", current->comm, current->pid); | ||
519 | } else { | ||
520 | printk | ||
521 | ("\nNo Valid pid - Either things are really messed up, or you are in the kernel\n"); | ||
522 | } | ||
523 | |||
524 | if (current->mm) { | ||
525 | printk("TEXT = 0x%p-0x%p DATA = 0x%p-0x%p\n" | ||
526 | "BSS = 0x%p-0x%p USER-STACK = 0x%p\n\n", | ||
527 | (void*)current->mm->start_code, | ||
528 | (void*)current->mm->end_code, | ||
529 | (void*)current->mm->start_data, | ||
530 | (void*)current->mm->end_data, | ||
531 | (void*)current->mm->end_data, | ||
532 | (void*)current->mm->brk, | ||
533 | (void*)current->mm->start_stack); | ||
534 | } | ||
535 | |||
536 | printk("return address: 0x%p; contents of [PC-16...PC+8]:\n", retaddr); | ||
537 | if (retaddr != 0 && retaddr <= (void*)physical_mem_end | ||
538 | #if L1_CODE_LENGTH != 0 | ||
539 | /* FIXME: Copy the code out of L1 Instruction SRAM through dma | ||
540 | memcpy. */ | ||
541 | && !(retaddr >= (void*)L1_CODE_START | ||
542 | && retaddr < (void*)(L1_CODE_START + L1_CODE_LENGTH)) | ||
543 | #endif | ||
544 | ) { | ||
545 | int i = 0; | ||
546 | unsigned short x = 0; | ||
547 | for (i = -16; i < 8; i++) { | ||
548 | if (get_user(x, (unsigned short *)retaddr + i)) | ||
549 | break; | ||
550 | #ifndef CONFIG_DEBUG_HWERR | ||
551 | /* If one of the last few instructions was a STI | ||
552 | * it is likily that the error occured awhile ago | ||
553 | * and we just noticed | ||
554 | */ | ||
555 | if (x >= 0x0040 && x <= 0x0047 && i <= 0) | ||
556 | panic("\n\nWARNING : You should reconfigure the kernel to turn on\n" | ||
557 | " 'Hardware error interrupt debugging'\n" | ||
558 | " The rest of this error is meanless\n"); | ||
559 | #endif | ||
560 | |||
561 | if (i == -8) | ||
562 | printk("\n"); | ||
563 | if (i == 0) | ||
564 | printk("X\n"); | ||
565 | printk("%04x ", x); | ||
566 | } | ||
567 | } else | ||
568 | printk("Cannot look at the [PC] for it is in unreadable L1 SRAM - sorry\n"); | ||
569 | |||
570 | printk("\n\n"); | ||
571 | |||
572 | printk("RETE: %08lx RETN: %08lx RETX: %08lx RETS: %08lx\n", | ||
573 | fp->rete, fp->retn, fp->retx, fp->rets); | ||
574 | printk("IPEND: %04lx SYSCFG: %04lx\n", fp->ipend, fp->syscfg); | ||
575 | printk("SEQSTAT: %08lx SP: %08lx\n", (long)fp->seqstat, (long)fp); | ||
576 | printk("R0: %08lx R1: %08lx R2: %08lx R3: %08lx\n", | ||
577 | fp->r0, fp->r1, fp->r2, fp->r3); | ||
578 | printk("R4: %08lx R5: %08lx R6: %08lx R7: %08lx\n", | ||
579 | fp->r4, fp->r5, fp->r6, fp->r7); | ||
580 | printk("P0: %08lx P1: %08lx P2: %08lx P3: %08lx\n", | ||
581 | fp->p0, fp->p1, fp->p2, fp->p3); | ||
582 | printk("P4: %08lx P5: %08lx FP: %08lx\n", fp->p4, fp->p5, fp->fp); | ||
583 | printk("A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n", | ||
584 | fp->a0w, fp->a0x, fp->a1w, fp->a1x); | ||
585 | |||
586 | printk("LB0: %08lx LT0: %08lx LC0: %08lx\n", fp->lb0, fp->lt0, | ||
587 | fp->lc0); | ||
588 | printk("LB1: %08lx LT1: %08lx LC1: %08lx\n", fp->lb1, fp->lt1, | ||
589 | fp->lc1); | ||
590 | printk("B0: %08lx L0: %08lx M0: %08lx I0: %08lx\n", fp->b0, fp->l0, | ||
591 | fp->m0, fp->i0); | ||
592 | printk("B1: %08lx L1: %08lx M1: %08lx I1: %08lx\n", fp->b1, fp->l1, | ||
593 | fp->m1, fp->i1); | ||
594 | printk("B2: %08lx L2: %08lx M2: %08lx I2: %08lx\n", fp->b2, fp->l2, | ||
595 | fp->m2, fp->i2); | ||
596 | printk("B3: %08lx L3: %08lx M3: %08lx I3: %08lx\n", fp->b3, fp->l3, | ||
597 | fp->m3, fp->i3); | ||
598 | |||
599 | printk("\nUSP: %08lx ASTAT: %08lx\n", rdusp(), fp->astat); | ||
600 | if ((long)fp->seqstat & SEQSTAT_EXCAUSE) { | ||
601 | printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void*)bfin_read_DCPLB_FAULT_ADDR()); | ||
602 | printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void*)bfin_read_ICPLB_FAULT_ADDR()); | ||
603 | } | ||
604 | |||
605 | printk("\n\n"); | ||
606 | } | ||
607 | |||
608 | #ifdef CONFIG_SYS_BFIN_SPINLOCK_L1 | ||
609 | asmlinkage int sys_bfin_spinlock(int *spinlock)__attribute__((l1_text)); | ||
610 | #endif | ||
611 | |||
612 | asmlinkage int sys_bfin_spinlock(int *spinlock) | ||
613 | { | ||
614 | int ret = 0; | ||
615 | int tmp = 0; | ||
616 | |||
617 | local_irq_disable(); | ||
618 | ret = get_user(tmp, spinlock); | ||
619 | if (ret == 0) { | ||
620 | if (tmp) | ||
621 | ret = 1; | ||
622 | tmp = 1; | ||
623 | put_user(tmp, spinlock); | ||
624 | } | ||
625 | local_irq_enable(); | ||
626 | return ret; | ||
627 | } | ||
628 | |||
629 | void panic_cplb_error(int cplb_panic, struct pt_regs *fp) | ||
630 | { | ||
631 | switch (cplb_panic) { | ||
632 | case CPLB_NO_UNLOCKED: | ||
633 | printk(KERN_EMERG "All CPLBs are locked\n"); | ||
634 | break; | ||
635 | case CPLB_PROT_VIOL: | ||
636 | return; | ||
637 | case CPLB_NO_ADDR_MATCH: | ||
638 | return; | ||
639 | case CPLB_UNKNOWN_ERR: | ||
640 | printk(KERN_EMERG "Unknown CPLB Exception\n"); | ||
641 | break; | ||
642 | } | ||
643 | |||
644 | printk(KERN_EMERG "DCPLB_FAULT_ADDR=%p\n", (void*)bfin_read_DCPLB_FAULT_ADDR()); | ||
645 | printk(KERN_EMERG "ICPLB_FAULT_ADDR=%p\n", (void*)bfin_read_ICPLB_FAULT_ADDR()); | ||
646 | dump_bfin_regs(fp, (void *)fp->retx); | ||
647 | dump_stack(); | ||
648 | panic("Unrecoverable event\n"); | ||
649 | } | ||
diff --git a/arch/blackfin/kernel/vmlinux.lds.S b/arch/blackfin/kernel/vmlinux.lds.S new file mode 100644 index 000000000000..6ae9ebbd8e58 --- /dev/null +++ b/arch/blackfin/kernel/vmlinux.lds.S | |||
@@ -0,0 +1,228 @@ | |||
1 | /* | ||
2 | * File: arch/blackfin/kernel/vmlinux.lds.S | ||
3 | * Based on: none - original work | ||
4 | * Author: | ||
5 | * | ||
6 | * Created: Tue Sep 21 2004 | ||
7 | * Description: Master linker script for blackfin architecture | ||
8 | * | ||
9 | * Modified: | ||
10 | * Copyright 2004-2006 Analog Devices Inc. | ||
11 | * | ||
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, see the file COPYING, or write | ||
26 | * to the Free Software Foundation, Inc., | ||
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
28 | */ | ||
29 | |||
30 | #define VMLINUX_SYMBOL(_sym_) _##_sym_ | ||
31 | |||
32 | #include <asm-generic/vmlinux.lds.h> | ||
33 | #include <asm/mem_map.h> | ||
34 | |||
35 | |||
36 | OUTPUT_FORMAT("elf32-bfin") | ||
37 | ENTRY(__start) | ||
38 | _jiffies = _jiffies_64; | ||
39 | |||
40 | MEMORY | ||
41 | { | ||
42 | ram : ORIGIN = CONFIG_BOOT_LOAD, LENGTH = (CONFIG_MEM_SIZE * 1024 * 1024) - (CONFIG_BOOT_LOAD) | ||
43 | l1_data_a : ORIGIN = L1_DATA_A_START, LENGTH = L1_DATA_A_LENGTH | ||
44 | l1_data_b : ORIGIN = L1_DATA_B_START, LENGTH = L1_DATA_B_LENGTH | ||
45 | l1_code : ORIGIN = L1_CODE_START, LENGTH = L1_CODE_LENGTH | ||
46 | l1_scratch : ORIGIN = L1_SCRATCH_START, LENGTH = L1_SCRATCH_LENGTH | ||
47 | } | ||
48 | |||
49 | SECTIONS | ||
50 | { | ||
51 | . = CONFIG_BOOT_LOAD; | ||
52 | |||
53 | .text : | ||
54 | { | ||
55 | _text = .; | ||
56 | __stext = .; | ||
57 | *(.text) | ||
58 | SCHED_TEXT | ||
59 | *(.text.lock) | ||
60 | . = ALIGN(16); | ||
61 | ___start___ex_table = .; | ||
62 | *(__ex_table) | ||
63 | ___stop___ex_table = .; | ||
64 | |||
65 | *($code) | ||
66 | *(.rodata) | ||
67 | *(.rodata.*) | ||
68 | *(__vermagic) /* Kernel version magic */ | ||
69 | *(.rodata1) | ||
70 | *(.fixup) | ||
71 | *(.spinlock.text) | ||
72 | |||
73 | /* Kernel symbol table: Normal symbols */ | ||
74 | . = ALIGN(4); | ||
75 | ___start___ksymtab = .; | ||
76 | *(__ksymtab) | ||
77 | ___stop___ksymtab = .; | ||
78 | |||
79 | /* Kernel symbol table: GPL-only symbols */ | ||
80 | ___start___ksymtab_gpl = .; | ||
81 | *(__ksymtab_gpl) | ||
82 | ___stop___ksymtab_gpl = .; | ||
83 | |||
84 | /* Kernel symbol table: Normal unused symbols */ \ | ||
85 | ___start___ksymtab_unused = .; | ||
86 | *(__ksymtab_unused) | ||
87 | ___stop___ksymtab_unused = .; | ||
88 | |||
89 | /* Kernel symbol table: GPL-only unused symbols */ | ||
90 | ___start___ksymtab_unused_gpl = .; | ||
91 | *(__ksymtab_unused_gpl) | ||
92 | ___stop___ksymtab_unused_gpl = .; | ||
93 | |||
94 | |||
95 | /* Kernel symbol table: GPL-future symbols */ | ||
96 | ___start___ksymtab_gpl_future = .; | ||
97 | *(__ksymtab_gpl_future) | ||
98 | ___stop___ksymtab_gpl_future = .; | ||
99 | |||
100 | /* Kernel symbol table: Normal symbols */ | ||
101 | ___start___kcrctab = .; | ||
102 | *(__kcrctab) | ||
103 | ___stop___kcrctab = .; | ||
104 | |||
105 | /* Kernel symbol table: GPL-only symbols */ | ||
106 | ___start___kcrctab_gpl = .; | ||
107 | *(__kcrctab_gpl) | ||
108 | ___stop___kcrctab_gpl = .; | ||
109 | |||
110 | /* Kernel symbol table: GPL-future symbols */ | ||
111 | ___start___kcrctab_gpl_future = .; | ||
112 | *(__kcrctab_gpl_future) | ||
113 | ___stop___kcrctab_gpl_future = .; | ||
114 | |||
115 | /* Kernel symbol table: strings */ | ||
116 | *(__ksymtab_strings) | ||
117 | |||
118 | . = ALIGN(4); | ||
119 | __etext = .; | ||
120 | } > ram | ||
121 | |||
122 | .init : | ||
123 | { | ||
124 | . = ALIGN(4096); | ||
125 | ___init_begin = .; | ||
126 | __sinittext = .; | ||
127 | *(.init.text) | ||
128 | __einittext = .; | ||
129 | *(.init.data) | ||
130 | . = ALIGN(16); | ||
131 | ___setup_start = .; | ||
132 | *(.init.setup) | ||
133 | ___setup_end = .; | ||
134 | ___start___param = .; | ||
135 | *(__param) | ||
136 | ___stop___param = .; | ||
137 | ___initcall_start = .; | ||
138 | INITCALLS | ||
139 | ___initcall_end = .; | ||
140 | ___con_initcall_start = .; | ||
141 | *(.con_initcall.init) | ||
142 | ___con_initcall_end = .; | ||
143 | ___security_initcall_start = .; | ||
144 | *(.security_initcall.init) | ||
145 | ___security_initcall_end = .; | ||
146 | . = ALIGN(4); | ||
147 | ___initramfs_start = .; | ||
148 | *(.init.ramfs) | ||
149 | ___initramfs_end = .; | ||
150 | . = ALIGN(4); | ||
151 | ___init_end = .; | ||
152 | } > ram | ||
153 | |||
154 | __l1_lma_start = .; | ||
155 | |||
156 | .text_l1 : | ||
157 | { | ||
158 | . = ALIGN(4); | ||
159 | __stext_l1 = .; | ||
160 | *(.l1.text) | ||
161 | |||
162 | . = ALIGN(4); | ||
163 | __etext_l1 = .; | ||
164 | } > l1_code AT > ram | ||
165 | |||
166 | .data_l1 : | ||
167 | { | ||
168 | . = ALIGN(4); | ||
169 | __sdata_l1 = .; | ||
170 | *(.l1.data) | ||
171 | __edata_l1 = .; | ||
172 | |||
173 | . = ALIGN(4); | ||
174 | __sbss_l1 = .; | ||
175 | *(.l1.bss) | ||
176 | |||
177 | . = ALIGN(32); | ||
178 | *(.data_l1.cacheline_aligned) | ||
179 | |||
180 | . = ALIGN(4); | ||
181 | __ebss_l1 = .; | ||
182 | } > l1_data_a AT > ram | ||
183 | .data_b_l1 : | ||
184 | { | ||
185 | . = ALIGN(4); | ||
186 | __sdata_b_l1 = .; | ||
187 | *(.l1.data.B) | ||
188 | __edata_b_l1 = .; | ||
189 | |||
190 | . = ALIGN(4); | ||
191 | __sbss_b_l1 = .; | ||
192 | *(.l1.bss.B) | ||
193 | |||
194 | . = ALIGN(4); | ||
195 | __ebss_b_l1 = .; | ||
196 | } > l1_data_b AT > ram | ||
197 | |||
198 | .data : | ||
199 | { | ||
200 | __sdata = .; | ||
201 | . = ALIGN(0x2000); | ||
202 | *(.data.init_task) | ||
203 | *(.data) | ||
204 | |||
205 | . = ALIGN(32); | ||
206 | *(.data.cacheline_aligned) | ||
207 | |||
208 | . = ALIGN(0x2000); | ||
209 | __edata = .; | ||
210 | } > ram | ||
211 | |||
212 | /DISCARD/ : { /* Exit code and data*/ | ||
213 | *(.exit.text) | ||
214 | *(.exit.data) | ||
215 | *(.exitcall.exit) | ||
216 | } > ram | ||
217 | |||
218 | .bss : | ||
219 | { | ||
220 | . = ALIGN(4); | ||
221 | ___bss_start = .; | ||
222 | *(.bss) | ||
223 | *(COMMON) | ||
224 | . = ALIGN(4); | ||
225 | ___bss_stop = .; | ||
226 | __end = . ; | ||
227 | } > ram | ||
228 | } | ||