aboutsummaryrefslogtreecommitdiffstats
path: root/arch/unicore32/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-03-17 13:11:25 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-03-17 13:11:25 -0400
commit7b7adc4a016a1decb806eb71ecab98721fa7f146 (patch)
tree0a6f9a6e5659faa94604fbc575382a18f143c657 /arch/unicore32/kernel
parent31598e8713ef501c8f6aad2e2ec8a9457e8877c1 (diff)
parent289d6b0e287e0acd85f3e6b7ea6c2cb5c234909a (diff)
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/epip/linux-2.6-unicore32
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/epip/linux-2.6-unicore32: (40 commits) unicore32: rewrite arch-specific tlb.h to use asm-generic version unicore32: modify io_p2v and io_v2p macros, and adjust PKUNITY_mmio_BASEs unicore32: replace unicore32-specific iomap functions with generic lib implementation unicore32 machine related: add frame buffer driver for pkunity-v3 soc unicore32 machine related files: add i2c bus drivers for pkunity-v3 soc unicore32 io: redefine __REG(x) and re-use readl/writel funcs unicore32 i8042 upgrade and bugfix: adjust resource request region type unicore32 upgrade to v2.6.38-rc5: add one more paramter for pte_alloc_map call unicore32 i8042: adjust io funcs of i8042-unicore32io.h unicore32: rename PKUNITY_IOSPACE_BASE to PKUNITY_MMIO_BASE unicore32: modify function names and parameters for irq_chips unicore32: remove unused lines in arch/unicore32/include/asm/irq.h unicore32 time.c: change calculate method for clock_event_device unicore32: ADD MAINTAINER for unicore32 architecture unicore32 machine related files: ps2 driver unicore32 machine related files: pci bus handling unicore32 machine related files: hardware registers unicore32 machine related files: core files unicore32 additional architecture files: boot process unicore32 additional architecture files: low-level lib: misc ... Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/unicore32/kernel')
-rw-r--r--arch/unicore32/kernel/Makefile33
-rw-r--r--arch/unicore32/kernel/asm-offsets.c112
-rw-r--r--arch/unicore32/kernel/clock.c390
-rw-r--r--arch/unicore32/kernel/cpu-ucv2.c93
-rw-r--r--arch/unicore32/kernel/debug-macro.S89
-rw-r--r--arch/unicore32/kernel/debug.S85
-rw-r--r--arch/unicore32/kernel/dma.c183
-rw-r--r--arch/unicore32/kernel/early_printk.c59
-rw-r--r--arch/unicore32/kernel/elf.c38
-rw-r--r--arch/unicore32/kernel/entry.S824
-rw-r--r--arch/unicore32/kernel/fpu-ucf64.c126
-rw-r--r--arch/unicore32/kernel/gpio.c122
-rw-r--r--arch/unicore32/kernel/head.S252
-rw-r--r--arch/unicore32/kernel/hibernate.c160
-rw-r--r--arch/unicore32/kernel/hibernate_asm.S117
-rw-r--r--arch/unicore32/kernel/init_task.c44
-rw-r--r--arch/unicore32/kernel/irq.c426
-rw-r--r--arch/unicore32/kernel/ksyms.c99
-rw-r--r--arch/unicore32/kernel/ksyms.h15
-rw-r--r--arch/unicore32/kernel/module.c152
-rw-r--r--arch/unicore32/kernel/pci.c404
-rw-r--r--arch/unicore32/kernel/pm.c123
-rw-r--r--arch/unicore32/kernel/process.c389
-rw-r--r--arch/unicore32/kernel/ptrace.c149
-rw-r--r--arch/unicore32/kernel/puv3-core.c285
-rw-r--r--arch/unicore32/kernel/puv3-nb0916.c145
-rw-r--r--arch/unicore32/kernel/pwm.c263
-rw-r--r--arch/unicore32/kernel/rtc.c380
-rw-r--r--arch/unicore32/kernel/setup.c360
-rw-r--r--arch/unicore32/kernel/setup.h30
-rw-r--r--arch/unicore32/kernel/signal.c494
-rw-r--r--arch/unicore32/kernel/sleep.S202
-rw-r--r--arch/unicore32/kernel/stacktrace.c131
-rw-r--r--arch/unicore32/kernel/sys.c126
-rw-r--r--arch/unicore32/kernel/time.c143
-rw-r--r--arch/unicore32/kernel/traps.c333
-rw-r--r--arch/unicore32/kernel/vmlinux.lds.S61
37 files changed, 7437 insertions, 0 deletions
diff --git a/arch/unicore32/kernel/Makefile b/arch/unicore32/kernel/Makefile
new file mode 100644
index 00000000000..ec23a2fb2f5
--- /dev/null
+++ b/arch/unicore32/kernel/Makefile
@@ -0,0 +1,33 @@
1#
2# Makefile for the linux kernel.
3#
4
5# Object file lists.
6obj-y := dma.o elf.o entry.o process.o ptrace.o
7obj-y += setup.o signal.o sys.o stacktrace.o traps.o
8
9obj-$(CONFIG_MODULES) += ksyms.o module.o
10obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
11
12obj-$(CONFIG_CPU_FREQ) += cpu-ucv2.o
13obj-$(CONFIG_UNICORE_FPU_F64) += fpu-ucf64.o
14
15# obj-y for architecture PKUnity v3
16obj-$(CONFIG_ARCH_PUV3) += clock.o irq.o time.o
17
18obj-$(CONFIG_PUV3_GPIO) += gpio.o
19obj-$(CONFIG_PUV3_RTC) += rtc.o
20obj-$(CONFIG_PUV3_PWM) += pwm.o
21obj-$(CONFIG_PUV3_PM) += pm.o sleep.o
22obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o
23
24obj-$(CONFIG_PCI) += pci.o
25
26# obj-y for specific machines
27obj-$(CONFIG_ARCH_PUV3) += puv3-core.o
28obj-$(CONFIG_PUV3_NB0916) += puv3-nb0916.o
29
30head-y := head.o
31obj-$(CONFIG_DEBUG_LL) += debug.o
32
33extra-y := $(head-y) init_task.o vmlinux.lds
diff --git a/arch/unicore32/kernel/asm-offsets.c b/arch/unicore32/kernel/asm-offsets.c
new file mode 100644
index 00000000000..ffcbe7536ca
--- /dev/null
+++ b/arch/unicore32/kernel/asm-offsets.c
@@ -0,0 +1,112 @@
1/*
2 * linux/arch/unicore32/kernel/asm-offsets.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * Generate definitions needed by assembly language modules.
9 * This code generates raw asm output which is post-processed to extract
10 * and format the required data.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License version 2 as
14 * published by the Free Software Foundation.
15 */
16#include <linux/sched.h>
17#include <linux/mm.h>
18#include <linux/dma-mapping.h>
19#include <linux/kbuild.h>
20#include <linux/suspend.h>
21#include <linux/thread_info.h>
22#include <asm/memory.h>
23#include <asm/suspend.h>
24
25/*
26 * GCC 3.0, 3.1: general bad code generation.
27 * GCC 3.2.0: incorrect function argument offset calculation.
28 * GCC 3.2.x: miscompiles NEW_AUX_ENT in fs/binfmt_elf.c
29 * (http://gcc.gnu.org/PR8896) and incorrect structure
30 * initialisation in fs/jffs2/erase.c
31 */
32#if (__GNUC__ < 4)
33#error Your compiler should upgrade to uc4
34#error Known good compilers: 4.2.2
35#endif
36
37int main(void)
38{
39 DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
40 BLANK();
41 DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
42 DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
43 DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
44 DEFINE(TI_TASK, offsetof(struct thread_info, task));
45 DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
46 DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
47 DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));
48 DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp));
49#ifdef CONFIG_UNICORE_FPU_F64
50 DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
51#endif
52 BLANK();
53 DEFINE(S_R0, offsetof(struct pt_regs, UCreg_00));
54 DEFINE(S_R1, offsetof(struct pt_regs, UCreg_01));
55 DEFINE(S_R2, offsetof(struct pt_regs, UCreg_02));
56 DEFINE(S_R3, offsetof(struct pt_regs, UCreg_03));
57 DEFINE(S_R4, offsetof(struct pt_regs, UCreg_04));
58 DEFINE(S_R5, offsetof(struct pt_regs, UCreg_05));
59 DEFINE(S_R6, offsetof(struct pt_regs, UCreg_06));
60 DEFINE(S_R7, offsetof(struct pt_regs, UCreg_07));
61 DEFINE(S_R8, offsetof(struct pt_regs, UCreg_08));
62 DEFINE(S_R9, offsetof(struct pt_regs, UCreg_09));
63 DEFINE(S_R10, offsetof(struct pt_regs, UCreg_10));
64 DEFINE(S_R11, offsetof(struct pt_regs, UCreg_11));
65 DEFINE(S_R12, offsetof(struct pt_regs, UCreg_12));
66 DEFINE(S_R13, offsetof(struct pt_regs, UCreg_13));
67 DEFINE(S_R14, offsetof(struct pt_regs, UCreg_14));
68 DEFINE(S_R15, offsetof(struct pt_regs, UCreg_15));
69 DEFINE(S_R16, offsetof(struct pt_regs, UCreg_16));
70 DEFINE(S_R17, offsetof(struct pt_regs, UCreg_17));
71 DEFINE(S_R18, offsetof(struct pt_regs, UCreg_18));
72 DEFINE(S_R19, offsetof(struct pt_regs, UCreg_19));
73 DEFINE(S_R20, offsetof(struct pt_regs, UCreg_20));
74 DEFINE(S_R21, offsetof(struct pt_regs, UCreg_21));
75 DEFINE(S_R22, offsetof(struct pt_regs, UCreg_22));
76 DEFINE(S_R23, offsetof(struct pt_regs, UCreg_23));
77 DEFINE(S_R24, offsetof(struct pt_regs, UCreg_24));
78 DEFINE(S_R25, offsetof(struct pt_regs, UCreg_25));
79 DEFINE(S_R26, offsetof(struct pt_regs, UCreg_26));
80 DEFINE(S_FP, offsetof(struct pt_regs, UCreg_fp));
81 DEFINE(S_IP, offsetof(struct pt_regs, UCreg_ip));
82 DEFINE(S_SP, offsetof(struct pt_regs, UCreg_sp));
83 DEFINE(S_LR, offsetof(struct pt_regs, UCreg_lr));
84 DEFINE(S_PC, offsetof(struct pt_regs, UCreg_pc));
85 DEFINE(S_PSR, offsetof(struct pt_regs, UCreg_asr));
86 DEFINE(S_OLD_R0, offsetof(struct pt_regs, UCreg_ORIG_00));
87 DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
88 BLANK();
89 DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
90 DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
91 BLANK();
92 DEFINE(VM_EXEC, VM_EXEC);
93 BLANK();
94 DEFINE(PAGE_SZ, PAGE_SIZE);
95 BLANK();
96 DEFINE(SYS_ERROR0, 0x9f0000);
97 BLANK();
98 DEFINE(PBE_ADDRESS, offsetof(struct pbe, address));
99 DEFINE(PBE_ORIN_ADDRESS, offsetof(struct pbe, orig_address));
100 DEFINE(PBE_NEXT, offsetof(struct pbe, next));
101 DEFINE(SWSUSP_CPU, offsetof(struct swsusp_arch_regs, \
102 cpu_context));
103#ifdef CONFIG_UNICORE_FPU_F64
104 DEFINE(SWSUSP_FPSTATE, offsetof(struct swsusp_arch_regs, \
105 fpstate));
106#endif
107 BLANK();
108 DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
109 DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE);
110 DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE);
111 return 0;
112}
diff --git a/arch/unicore32/kernel/clock.c b/arch/unicore32/kernel/clock.c
new file mode 100644
index 00000000000..18d4563e6fa
--- /dev/null
+++ b/arch/unicore32/kernel/clock.c
@@ -0,0 +1,390 @@
1/*
2 * linux/arch/unicore32/kernel/clock.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/device.h>
16#include <linux/list.h>
17#include <linux/errno.h>
18#include <linux/err.h>
19#include <linux/string.h>
20#include <linux/clk.h>
21#include <linux/mutex.h>
22#include <linux/delay.h>
23#include <linux/io.h>
24
25#include <mach/hardware.h>
26
27/*
28 * Very simple clock implementation
29 */
30struct clk {
31 struct list_head node;
32 unsigned long rate;
33 const char *name;
34};
35
36static struct clk clk_ost_clk = {
37 .name = "OST_CLK",
38 .rate = CLOCK_TICK_RATE,
39};
40
41static struct clk clk_mclk_clk = {
42 .name = "MAIN_CLK",
43};
44
45static struct clk clk_bclk32_clk = {
46 .name = "BUS32_CLK",
47};
48
49static struct clk clk_ddr_clk = {
50 .name = "DDR_CLK",
51};
52
53static struct clk clk_vga_clk = {
54 .name = "VGA_CLK",
55};
56
57static LIST_HEAD(clocks);
58static DEFINE_MUTEX(clocks_mutex);
59
60struct clk *clk_get(struct device *dev, const char *id)
61{
62 struct clk *p, *clk = ERR_PTR(-ENOENT);
63
64 mutex_lock(&clocks_mutex);
65 list_for_each_entry(p, &clocks, node) {
66 if (strcmp(id, p->name) == 0) {
67 clk = p;
68 break;
69 }
70 }
71 mutex_unlock(&clocks_mutex);
72
73 return clk;
74}
75EXPORT_SYMBOL(clk_get);
76
77void clk_put(struct clk *clk)
78{
79}
80EXPORT_SYMBOL(clk_put);
81
82int clk_enable(struct clk *clk)
83{
84 return 0;
85}
86EXPORT_SYMBOL(clk_enable);
87
88void clk_disable(struct clk *clk)
89{
90}
91EXPORT_SYMBOL(clk_disable);
92
93unsigned long clk_get_rate(struct clk *clk)
94{
95 return clk->rate;
96}
97EXPORT_SYMBOL(clk_get_rate);
98
99struct {
100 unsigned long rate;
101 unsigned long cfg;
102 unsigned long div;
103} vga_clk_table[] = {
104 {.rate = 25175000, .cfg = 0x00002001, .div = 0x9},
105 {.rate = 31500000, .cfg = 0x00002001, .div = 0x7},
106 {.rate = 40000000, .cfg = 0x00003801, .div = 0x9},
107 {.rate = 49500000, .cfg = 0x00003801, .div = 0x7},
108 {.rate = 65000000, .cfg = 0x00002c01, .div = 0x4},
109 {.rate = 78750000, .cfg = 0x00002400, .div = 0x7},
110 {.rate = 108000000, .cfg = 0x00002c01, .div = 0x2},
111 {.rate = 106500000, .cfg = 0x00003c01, .div = 0x3},
112 {.rate = 50650000, .cfg = 0x00106400, .div = 0x9},
113 {.rate = 61500000, .cfg = 0x00106400, .div = 0xa},
114 {.rate = 85500000, .cfg = 0x00002800, .div = 0x6},
115};
116
117struct {
118 unsigned long mrate;
119 unsigned long prate;
120} mclk_clk_table[] = {
121 {.mrate = 500000000, .prate = 0x00109801},
122 {.mrate = 525000000, .prate = 0x00104C00},
123 {.mrate = 550000000, .prate = 0x00105000},
124 {.mrate = 575000000, .prate = 0x00105400},
125 {.mrate = 600000000, .prate = 0x00105800},
126 {.mrate = 625000000, .prate = 0x00105C00},
127 {.mrate = 650000000, .prate = 0x00106000},
128 {.mrate = 675000000, .prate = 0x00106400},
129 {.mrate = 700000000, .prate = 0x00106800},
130 {.mrate = 725000000, .prate = 0x00106C00},
131 {.mrate = 750000000, .prate = 0x00107000},
132 {.mrate = 775000000, .prate = 0x00107400},
133 {.mrate = 800000000, .prate = 0x00107800},
134};
135
136int clk_set_rate(struct clk *clk, unsigned long rate)
137{
138 if (clk == &clk_vga_clk) {
139 unsigned long pll_vgacfg, pll_vgadiv;
140 int ret, i;
141
142 /* lookup vga_clk_table */
143 ret = -EINVAL;
144 for (i = 0; i < ARRAY_SIZE(vga_clk_table); i++) {
145 if (rate == vga_clk_table[i].rate) {
146 pll_vgacfg = vga_clk_table[i].cfg;
147 pll_vgadiv = vga_clk_table[i].div;
148 ret = 0;
149 break;
150 }
151 }
152
153 if (ret)
154 return ret;
155
156 if (readl(PM_PLLVGACFG) == pll_vgacfg)
157 return 0;
158
159 /* set pll vga cfg reg. */
160 writel(pll_vgacfg, PM_PLLVGACFG);
161
162 writel(PM_PMCR_CFBVGA, PM_PMCR);
163 while ((readl(PM_PLLDFCDONE) & PM_PLLDFCDONE_VGADFC)
164 != PM_PLLDFCDONE_VGADFC)
165 udelay(100); /* about 1ms */
166
167 /* set div cfg reg. */
168 writel(readl(PM_PCGR) | PM_PCGR_VGACLK, PM_PCGR);
169
170 writel((readl(PM_DIVCFG) & ~PM_DIVCFG_VGACLK_MASK)
171 | PM_DIVCFG_VGACLK(pll_vgadiv), PM_DIVCFG);
172
173 writel(readl(PM_SWRESET) | PM_SWRESET_VGADIV, PM_SWRESET);
174 while ((readl(PM_SWRESET) & PM_SWRESET_VGADIV)
175 == PM_SWRESET_VGADIV)
176 udelay(100); /* 65536 bclk32, about 320us */
177
178 writel(readl(PM_PCGR) & ~PM_PCGR_VGACLK, PM_PCGR);
179 }
180#ifdef CONFIG_CPU_FREQ
181 if (clk == &clk_mclk_clk) {
182 u32 pll_rate, divstatus = PM_DIVSTATUS;
183 int ret, i;
184
185 /* lookup mclk_clk_table */
186 ret = -EINVAL;
187 for (i = 0; i < ARRAY_SIZE(mclk_clk_table); i++) {
188 if (rate == mclk_clk_table[i].mrate) {
189 pll_rate = mclk_clk_table[i].prate;
190 clk_mclk_clk.rate = mclk_clk_table[i].mrate;
191 ret = 0;
192 break;
193 }
194 }
195
196 if (ret)
197 return ret;
198
199 if (clk_mclk_clk.rate)
200 clk_bclk32_clk.rate = clk_mclk_clk.rate
201 / (((divstatus & 0x0000f000) >> 12) + 1);
202
203 /* set pll sys cfg reg. */
204 PM_PLLSYSCFG = pll_rate;
205
206 PM_PMCR = PM_PMCR_CFBSYS;
207 while ((PM_PLLDFCDONE & PM_PLLDFCDONE_SYSDFC)
208 != PM_PLLDFCDONE_SYSDFC)
209 udelay(100);
210 /* about 1ms */
211 }
212#endif
213 return 0;
214}
215EXPORT_SYMBOL(clk_set_rate);
216
217int clk_register(struct clk *clk)
218{
219 mutex_lock(&clocks_mutex);
220 list_add(&clk->node, &clocks);
221 mutex_unlock(&clocks_mutex);
222 printk(KERN_DEFAULT "PKUnity PM: %s %lu.%02luM\n", clk->name,
223 (clk->rate)/1000000, (clk->rate)/10000 % 100);
224 return 0;
225}
226EXPORT_SYMBOL(clk_register);
227
228void clk_unregister(struct clk *clk)
229{
230 mutex_lock(&clocks_mutex);
231 list_del(&clk->node);
232 mutex_unlock(&clocks_mutex);
233}
234EXPORT_SYMBOL(clk_unregister);
235
236struct {
237 unsigned long prate;
238 unsigned long rate;
239} pllrate_table[] = {
240 {.prate = 0x00002001, .rate = 250000000},
241 {.prate = 0x00104801, .rate = 250000000},
242 {.prate = 0x00104C01, .rate = 262500000},
243 {.prate = 0x00002401, .rate = 275000000},
244 {.prate = 0x00105001, .rate = 275000000},
245 {.prate = 0x00105401, .rate = 287500000},
246 {.prate = 0x00002801, .rate = 300000000},
247 {.prate = 0x00105801, .rate = 300000000},
248 {.prate = 0x00105C01, .rate = 312500000},
249 {.prate = 0x00002C01, .rate = 325000000},
250 {.prate = 0x00106001, .rate = 325000000},
251 {.prate = 0x00106401, .rate = 337500000},
252 {.prate = 0x00003001, .rate = 350000000},
253 {.prate = 0x00106801, .rate = 350000000},
254 {.prate = 0x00106C01, .rate = 362500000},
255 {.prate = 0x00003401, .rate = 375000000},
256 {.prate = 0x00107001, .rate = 375000000},
257 {.prate = 0x00107401, .rate = 387500000},
258 {.prate = 0x00003801, .rate = 400000000},
259 {.prate = 0x00107801, .rate = 400000000},
260 {.prate = 0x00107C01, .rate = 412500000},
261 {.prate = 0x00003C01, .rate = 425000000},
262 {.prate = 0x00108001, .rate = 425000000},
263 {.prate = 0x00108401, .rate = 437500000},
264 {.prate = 0x00004001, .rate = 450000000},
265 {.prate = 0x00108801, .rate = 450000000},
266 {.prate = 0x00108C01, .rate = 462500000},
267 {.prate = 0x00004401, .rate = 475000000},
268 {.prate = 0x00109001, .rate = 475000000},
269 {.prate = 0x00109401, .rate = 487500000},
270 {.prate = 0x00004801, .rate = 500000000},
271 {.prate = 0x00109801, .rate = 500000000},
272 {.prate = 0x00104C00, .rate = 525000000},
273 {.prate = 0x00002400, .rate = 550000000},
274 {.prate = 0x00105000, .rate = 550000000},
275 {.prate = 0x00105400, .rate = 575000000},
276 {.prate = 0x00002800, .rate = 600000000},
277 {.prate = 0x00105800, .rate = 600000000},
278 {.prate = 0x00105C00, .rate = 625000000},
279 {.prate = 0x00002C00, .rate = 650000000},
280 {.prate = 0x00106000, .rate = 650000000},
281 {.prate = 0x00106400, .rate = 675000000},
282 {.prate = 0x00003000, .rate = 700000000},
283 {.prate = 0x00106800, .rate = 700000000},
284 {.prate = 0x00106C00, .rate = 725000000},
285 {.prate = 0x00003400, .rate = 750000000},
286 {.prate = 0x00107000, .rate = 750000000},
287 {.prate = 0x00107400, .rate = 775000000},
288 {.prate = 0x00003800, .rate = 800000000},
289 {.prate = 0x00107800, .rate = 800000000},
290 {.prate = 0x00107C00, .rate = 825000000},
291 {.prate = 0x00003C00, .rate = 850000000},
292 {.prate = 0x00108000, .rate = 850000000},
293 {.prate = 0x00108400, .rate = 875000000},
294 {.prate = 0x00004000, .rate = 900000000},
295 {.prate = 0x00108800, .rate = 900000000},
296 {.prate = 0x00108C00, .rate = 925000000},
297 {.prate = 0x00004400, .rate = 950000000},
298 {.prate = 0x00109000, .rate = 950000000},
299 {.prate = 0x00109400, .rate = 975000000},
300 {.prate = 0x00004800, .rate = 1000000000},
301 {.prate = 0x00109800, .rate = 1000000000},
302};
303
304struct {
305 unsigned long prate;
306 unsigned long drate;
307} pddr_table[] = {
308 {.prate = 0x00100800, .drate = 44236800},
309 {.prate = 0x00100C00, .drate = 66355200},
310 {.prate = 0x00101000, .drate = 88473600},
311 {.prate = 0x00101400, .drate = 110592000},
312 {.prate = 0x00101800, .drate = 132710400},
313 {.prate = 0x00101C01, .drate = 154828800},
314 {.prate = 0x00102001, .drate = 176947200},
315 {.prate = 0x00102401, .drate = 199065600},
316 {.prate = 0x00102801, .drate = 221184000},
317 {.prate = 0x00102C01, .drate = 243302400},
318 {.prate = 0x00103001, .drate = 265420800},
319 {.prate = 0x00103401, .drate = 287539200},
320 {.prate = 0x00103801, .drate = 309657600},
321 {.prate = 0x00103C01, .drate = 331776000},
322 {.prate = 0x00104001, .drate = 353894400},
323};
324
325static int __init clk_init(void)
326{
327#ifdef CONFIG_PUV3_PM
328 u32 pllrate, divstatus = readl(PM_DIVSTATUS);
329 u32 pcgr_val = readl(PM_PCGR);
330 int i;
331
332 pcgr_val |= PM_PCGR_BCLKMME | PM_PCGR_BCLKH264E | PM_PCGR_BCLKH264D
333 | PM_PCGR_HECLK | PM_PCGR_HDCLK;
334 writel(pcgr_val, PM_PCGR);
335
336 pllrate = readl(PM_PLLSYSSTATUS);
337
338 /* lookup pmclk_table */
339 clk_mclk_clk.rate = 0;
340 for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) {
341 if (pllrate == pllrate_table[i].prate) {
342 clk_mclk_clk.rate = pllrate_table[i].rate;
343 break;
344 }
345 }
346
347 if (clk_mclk_clk.rate)
348 clk_bclk32_clk.rate = clk_mclk_clk.rate /
349 (((divstatus & 0x0000f000) >> 12) + 1);
350
351 pllrate = readl(PM_PLLDDRSTATUS);
352
353 /* lookup pddr_table */
354 clk_ddr_clk.rate = 0;
355 for (i = 0; i < ARRAY_SIZE(pddr_table); i++) {
356 if (pllrate == pddr_table[i].prate) {
357 clk_ddr_clk.rate = pddr_table[i].drate;
358 break;
359 }
360 }
361
362 pllrate = readl(PM_PLLVGASTATUS);
363
364 /* lookup pvga_table */
365 clk_vga_clk.rate = 0;
366 for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) {
367 if (pllrate == pllrate_table[i].prate) {
368 clk_vga_clk.rate = pllrate_table[i].rate;
369 break;
370 }
371 }
372
373 if (clk_vga_clk.rate)
374 clk_vga_clk.rate = clk_vga_clk.rate /
375 (((divstatus & 0x00f00000) >> 20) + 1);
376
377 clk_register(&clk_vga_clk);
378#endif
379#ifdef CONFIG_ARCH_FPGA
380 clk_ddr_clk.rate = 33000000;
381 clk_mclk_clk.rate = 33000000;
382 clk_bclk32_clk.rate = 33000000;
383#endif
384 clk_register(&clk_ddr_clk);
385 clk_register(&clk_mclk_clk);
386 clk_register(&clk_bclk32_clk);
387 clk_register(&clk_ost_clk);
388 return 0;
389}
390core_initcall(clk_init);
diff --git a/arch/unicore32/kernel/cpu-ucv2.c b/arch/unicore32/kernel/cpu-ucv2.c
new file mode 100644
index 00000000000..4a99f62584c
--- /dev/null
+++ b/arch/unicore32/kernel/cpu-ucv2.c
@@ -0,0 +1,93 @@
1/*
2 * linux/arch/unicore32/kernel/cpu-ucv2.c: clock scaling for the UniCore-II
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/kernel.h>
15#include <linux/types.h>
16#include <linux/init.h>
17#include <linux/clk.h>
18#include <linux/cpufreq.h>
19
20#include <mach/hardware.h>
21
22static struct cpufreq_driver ucv2_driver;
23
24/* make sure that only the "userspace" governor is run
25 * -- anything else wouldn't make sense on this platform, anyway.
26 */
27int ucv2_verify_speed(struct cpufreq_policy *policy)
28{
29 if (policy->cpu)
30 return -EINVAL;
31
32 cpufreq_verify_within_limits(policy,
33 policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
34
35 return 0;
36}
37
38static unsigned int ucv2_getspeed(unsigned int cpu)
39{
40 struct clk *mclk = clk_get(NULL, "MAIN_CLK");
41
42 if (cpu)
43 return 0;
44 return clk_get_rate(mclk)/1000;
45}
46
47static int ucv2_target(struct cpufreq_policy *policy,
48 unsigned int target_freq,
49 unsigned int relation)
50{
51 unsigned int cur = ucv2_getspeed(0);
52 struct cpufreq_freqs freqs;
53 struct clk *mclk = clk_get(NULL, "MAIN_CLK");
54
55 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
56
57 if (!clk_set_rate(mclk, target_freq * 1000)) {
58 freqs.old = cur;
59 freqs.new = target_freq;
60 freqs.cpu = 0;
61 }
62
63 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
64
65 return 0;
66}
67
68static int __init ucv2_cpu_init(struct cpufreq_policy *policy)
69{
70 if (policy->cpu != 0)
71 return -EINVAL;
72 policy->cur = ucv2_getspeed(0);
73 policy->min = policy->cpuinfo.min_freq = 250000;
74 policy->max = policy->cpuinfo.max_freq = 1000000;
75 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
76 return 0;
77}
78
79static struct cpufreq_driver ucv2_driver = {
80 .flags = CPUFREQ_STICKY,
81 .verify = ucv2_verify_speed,
82 .target = ucv2_target,
83 .get = ucv2_getspeed,
84 .init = ucv2_cpu_init,
85 .name = "UniCore-II",
86};
87
88static int __init ucv2_cpufreq_init(void)
89{
90 return cpufreq_register_driver(&ucv2_driver);
91}
92
93arch_initcall(ucv2_cpufreq_init);
diff --git a/arch/unicore32/kernel/debug-macro.S b/arch/unicore32/kernel/debug-macro.S
new file mode 100644
index 00000000000..2711d6d87d8
--- /dev/null
+++ b/arch/unicore32/kernel/debug-macro.S
@@ -0,0 +1,89 @@
1/*
2 * linux/arch/unicore32/kernel/debug-macro.S
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * Debugging macro include header
13 */
14#include <generated/asm-offsets.h>
15#include <mach/hardware.h>
16
17 .macro put_word_ocd, rd, rx=r16
181001: movc \rx, p1.c0, #0
19 cand.a \rx, #2
20 bne 1001b
21 movc p1.c1, \rd, #1
22 .endm
23
24#ifdef CONFIG_DEBUG_OCD
25 /* debug using UniCore On-Chip-Debugger */
26 .macro addruart, rx
27 .endm
28
29 .macro senduart, rd, rx
30 put_word_ocd \rd, \rx
31 .endm
32
33 .macro busyuart, rd, rx
34 .endm
35
36 .macro waituart, rd, rx
37 .endm
38#else
39#define UART_CLK_DEFAULT 3686400 * 20
40 /* Uartclk = MCLK/ 2, The MCLK on my board is 3686400 * 40 */
41#define BAUD_RATE_DEFAULT 115200
42 /* The baud rate of the serial port */
43
44#define UART_DIVISOR_DEFAULT (UART_CLK_DEFAULT \
45 / (16 * BAUD_RATE_DEFAULT) - 1)
46
47 .macro addruart,rx
48 mrc p0, #0, \rx, c1, c0
49 tst \rx, #1 @ MMU enabled?
50 moveq \rx, #0xee000000 @ physical base address
51 movne \rx, #0x6e000000 @ virtual address
52
53 @ We probe for the active serial port here
54 @ However, now we assume UART0 is active: epip4d
55 @ We assume r1 and r2 can be clobbered.
56
57 movl r2, #UART_DIVISOR_DEFAULT
58 mov r1, #0x80
59 str r1, [\rx, #UART_LCR_OFFSET]
60 and r1, r2, #0xff00
61 mov r1, r1, lsr #8
62 str r1, [\rx, #UART_DLH_OFFSET]
63 and r1, r2, #0xff
64 str r1, [\rx, #UART_DLL_OFFSET]
65 mov r1, #0x7
66 str r1, [\rx, #UART_FCR_OFFSET]
67 mov r1, #0x3
68 str r1, [\rx, #UART_LCR_OFFSET]
69 mov r1, #0x0
70 str r1, [\rx, #UART_IER_OFFSET]
71 .endm
72
73 .macro senduart,rd,rx
74 str \rd, [\rx, #UART_THR_OFFSET]
75 .endm
76
77 .macro waituart,rd,rx
781001: ldr \rd, [\rx, #UART_LSR_OFFSET]
79 tst \rd, #UART_LSR_THRE
80 beq 1001b
81 .endm
82
83 .macro busyuart,rd,rx
841001: ldr \rd, [\rx, #UART_LSR_OFFSET]
85 tst \rd, #UART_LSR_TEMT
86 bne 1001b
87 .endm
88#endif
89
diff --git a/arch/unicore32/kernel/debug.S b/arch/unicore32/kernel/debug.S
new file mode 100644
index 00000000000..029fd12f6ab
--- /dev/null
+++ b/arch/unicore32/kernel/debug.S
@@ -0,0 +1,85 @@
1/*
2 * linux/arch/unicore32/kernel/debug.S
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * 32-bit debugging code
13 */
14#include <linux/linkage.h>
15#include <asm/assembler.h>
16
17 .text
18
19/*
20 * Some debugging routines (useful if you've got MM problems and
21 * printk isn't working). For DEBUGGING ONLY!!! Do not leave
22 * references to these in a production kernel!
23 */
24#include "debug-macro.S"
25
26/*
27 * Useful debugging routines
28 */
29ENTRY(printhex8)
30 mov r1, #8
31 b printhex
32ENDPROC(printhex8)
33
34ENTRY(printhex4)
35 mov r1, #4
36 b printhex
37ENDPROC(printhex4)
38
39ENTRY(printhex2)
40 mov r1, #2
41printhex: adr r2, hexbuf
42 add r3, r2, r1
43 mov r1, #0
44 stb r1, [r3]
451: and r1, r0, #15
46 mov r0, r0 >> #4
47 csub.a r1, #10
48 beg 2f
49 add r1, r1, #'0' - 'a' + 10
502: add r1, r1, #'a' - 10
51 stb.w r1, [r3+], #-1
52 cxor.a r3, r2
53 bne 1b
54 mov r0, r2
55 b printascii
56ENDPROC(printhex2)
57
58 .ltorg
59
60ENTRY(printascii)
61 addruart r3
62 b 2f
631: waituart r2, r3
64 senduart r1, r3
65 busyuart r2, r3
66 cxor.a r1, #'\n'
67 cmoveq r1, #'\r'
68 beq 1b
692: cxor.a r0, #0
70 beq 3f
71 ldb.w r1, [r0]+, #1
72 cxor.a r1, #0
73 bne 1b
743: mov pc, lr
75ENDPROC(printascii)
76
77ENTRY(printch)
78 addruart r3
79 mov r1, r0
80 mov r0, #0
81 b 1b
82ENDPROC(printch)
83
84hexbuf: .space 16
85
diff --git a/arch/unicore32/kernel/dma.c b/arch/unicore32/kernel/dma.c
new file mode 100644
index 00000000000..ae441bc3122
--- /dev/null
+++ b/arch/unicore32/kernel/dma.c
@@ -0,0 +1,183 @@
1/*
2 * linux/arch/unicore32/kernel/dma.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/kernel.h>
17#include <linux/interrupt.h>
18#include <linux/errno.h>
19#include <linux/io.h>
20
21#include <asm/system.h>
22#include <asm/irq.h>
23#include <mach/hardware.h>
24#include <mach/dma.h>
25
26struct dma_channel {
27 char *name;
28 puv3_dma_prio prio;
29 void (*irq_handler)(int, void *);
30 void (*err_handler)(int, void *);
31 void *data;
32};
33
34static struct dma_channel dma_channels[MAX_DMA_CHANNELS];
35
36int puv3_request_dma(char *name, puv3_dma_prio prio,
37 void (*irq_handler)(int, void *),
38 void (*err_handler)(int, void *),
39 void *data)
40{
41 unsigned long flags;
42 int i, found = 0;
43
44 /* basic sanity checks */
45 if (!name)
46 return -EINVAL;
47
48 local_irq_save(flags);
49
50 do {
51 /* try grabbing a DMA channel with the requested priority */
52 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
53 if ((dma_channels[i].prio == prio) &&
54 !dma_channels[i].name) {
55 found = 1;
56 break;
57 }
58 }
59 /* if requested prio group is full, try a hier priority */
60 } while (!found && prio--);
61
62 if (found) {
63 dma_channels[i].name = name;
64 dma_channels[i].irq_handler = irq_handler;
65 dma_channels[i].err_handler = err_handler;
66 dma_channels[i].data = data;
67 } else {
68 printk(KERN_WARNING "No more available DMA channels for %s\n",
69 name);
70 i = -ENODEV;
71 }
72
73 local_irq_restore(flags);
74 return i;
75}
76EXPORT_SYMBOL(puv3_request_dma);
77
78void puv3_free_dma(int dma_ch)
79{
80 unsigned long flags;
81
82 if (!dma_channels[dma_ch].name) {
83 printk(KERN_CRIT
84 "%s: trying to free channel %d which is already freed\n",
85 __func__, dma_ch);
86 return;
87 }
88
89 local_irq_save(flags);
90 dma_channels[dma_ch].name = NULL;
91 dma_channels[dma_ch].err_handler = NULL;
92 local_irq_restore(flags);
93}
94EXPORT_SYMBOL(puv3_free_dma);
95
96static irqreturn_t dma_irq_handler(int irq, void *dev_id)
97{
98 int i, dint;
99
100 dint = readl(DMAC_ITCSR);
101 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
102 if (dint & DMAC_CHANNEL(i)) {
103 struct dma_channel *channel = &dma_channels[i];
104
105 /* Clear TC interrupt of channel i */
106 writel(DMAC_CHANNEL(i), DMAC_ITCCR);
107 writel(0, DMAC_ITCCR);
108
109 if (channel->name && channel->irq_handler) {
110 channel->irq_handler(i, channel->data);
111 } else {
112 /*
113 * IRQ for an unregistered DMA channel:
114 * let's clear the interrupts and disable it.
115 */
116 printk(KERN_WARNING "spurious IRQ for"
117 " DMA channel %d\n", i);
118 }
119 }
120 }
121 return IRQ_HANDLED;
122}
123
124static irqreturn_t dma_err_handler(int irq, void *dev_id)
125{
126 int i, dint;
127
128 dint = readl(DMAC_IESR);
129 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
130 if (dint & DMAC_CHANNEL(i)) {
131 struct dma_channel *channel = &dma_channels[i];
132
133 /* Clear Err interrupt of channel i */
134 writel(DMAC_CHANNEL(i), DMAC_IECR);
135 writel(0, DMAC_IECR);
136
137 if (channel->name && channel->err_handler) {
138 channel->err_handler(i, channel->data);
139 } else {
140 /*
141 * IRQ for an unregistered DMA channel:
142 * let's clear the interrupts and disable it.
143 */
144 printk(KERN_WARNING "spurious IRQ for"
145 " DMA channel %d\n", i);
146 }
147 }
148 }
149 return IRQ_HANDLED;
150}
151
152int __init puv3_init_dma(void)
153{
154 int i, ret;
155
156 /* dma channel priorities on v8 processors:
157 * ch 0 - 1 <--> (0) DMA_PRIO_HIGH
158 * ch 2 - 3 <--> (1) DMA_PRIO_MEDIUM
159 * ch 4 - 5 <--> (2) DMA_PRIO_LOW
160 */
161 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
162 puv3_stop_dma(i);
163 dma_channels[i].name = NULL;
164 dma_channels[i].prio = min((i & 0x7) >> 1, DMA_PRIO_LOW);
165 }
166
167 ret = request_irq(IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
168 if (ret) {
169 printk(KERN_CRIT "Can't register IRQ for DMA\n");
170 return ret;
171 }
172
173 ret = request_irq(IRQ_DMAERR, dma_err_handler, 0, "DMAERR", NULL);
174 if (ret) {
175 printk(KERN_CRIT "Can't register IRQ for DMAERR\n");
176 free_irq(IRQ_DMA, "DMA");
177 return ret;
178 }
179
180 return 0;
181}
182
183postcore_initcall(puv3_init_dma);
diff --git a/arch/unicore32/kernel/early_printk.c b/arch/unicore32/kernel/early_printk.c
new file mode 100644
index 00000000000..3922255f1fa
--- /dev/null
+++ b/arch/unicore32/kernel/early_printk.c
@@ -0,0 +1,59 @@
1/*
2 * linux/arch/unicore32/kernel/early_printk.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/console.h>
13#include <linux/init.h>
14#include <linux/string.h>
15#include <mach/ocd.h>
16
17/* On-Chip-Debugger functions */
18
19static void early_ocd_write(struct console *con, const char *s, unsigned n)
20{
21 while (*s && n-- > 0) {
22 if (*s == '\n')
23 ocd_putc((int)'\r');
24 ocd_putc((int)*s);
25 s++;
26 }
27}
28
29static struct console early_ocd_console = {
30 .name = "earlyocd",
31 .write = early_ocd_write,
32 .flags = CON_PRINTBUFFER,
33 .index = -1,
34};
35
36/* Direct interface for emergencies */
37static struct console *early_console = &early_ocd_console;
38
39static int __initdata keep_early;
40
41static int __init setup_early_printk(char *buf)
42{
43 if (!buf)
44 return 0;
45
46 if (strstr(buf, "keep"))
47 keep_early = 1;
48
49 if (!strncmp(buf, "ocd", 3))
50 early_console = &early_ocd_console;
51
52 if (keep_early)
53 early_console->flags &= ~CON_BOOT;
54 else
55 early_console->flags |= CON_BOOT;
56 register_console(early_console);
57 return 0;
58}
59early_param("earlyprintk", setup_early_printk);
diff --git a/arch/unicore32/kernel/elf.c b/arch/unicore32/kernel/elf.c
new file mode 100644
index 00000000000..0a176734fef
--- /dev/null
+++ b/arch/unicore32/kernel/elf.c
@@ -0,0 +1,38 @@
1/*
2 * linux/arch/unicore32/kernel/elf.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/sched.h>
14#include <linux/personality.h>
15#include <linux/binfmts.h>
16#include <linux/elf.h>
17
18int elf_check_arch(const struct elf32_hdr *x)
19{
20 /* Make sure it's an UniCore executable */
21 if (x->e_machine != EM_UNICORE)
22 return 0;
23
24 /* Make sure the entry address is reasonable */
25 if (x->e_entry & 3)
26 return 0;
27
28 return 1;
29}
30EXPORT_SYMBOL(elf_check_arch);
31
32void elf_set_personality(const struct elf32_hdr *x)
33{
34 unsigned int personality = PER_LINUX;
35
36 set_personality(personality);
37}
38EXPORT_SYMBOL(elf_set_personality);
diff --git a/arch/unicore32/kernel/entry.S b/arch/unicore32/kernel/entry.S
new file mode 100644
index 00000000000..00a259f9819
--- /dev/null
+++ b/arch/unicore32/kernel/entry.S
@@ -0,0 +1,824 @@
1/*
2 * linux/arch/unicore32/kernel/entry.S
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * Low-level vector interface routines
13 */
14#include <linux/init.h>
15#include <linux/linkage.h>
16#include <asm/assembler.h>
17#include <asm/errno.h>
18#include <asm/thread_info.h>
19#include <asm/memory.h>
20#include <asm/unistd.h>
21#include <generated/asm-offsets.h>
22#include "debug-macro.S"
23
24@
25@ Most of the stack format comes from struct pt_regs, but with
26@ the addition of 8 bytes for storing syscall args 5 and 6.
27@
28#define S_OFF 8
29
30/*
31 * The SWI code relies on the fact that R0 is at the bottom of the stack
32 * (due to slow/fast restore user regs).
33 */
34#if S_R0 != 0
35#error "Please fix"
36#endif
37
38 .macro zero_fp
39#ifdef CONFIG_FRAME_POINTER
40 mov fp, #0
41#endif
42 .endm
43
44 .macro alignment_trap, rtemp
45#ifdef CONFIG_ALIGNMENT_TRAP
46 ldw \rtemp, .LCcralign
47 ldw \rtemp, [\rtemp]
48 movc p0.c1, \rtemp, #0
49#endif
50 .endm
51
52 .macro load_user_sp_lr, rd, rtemp, offset = 0
53 mov \rtemp, asr
54 xor \rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE)
55 mov.a asr, \rtemp @ switch to the SUSR mode
56
57 ldw sp, [\rd+], #\offset @ load sp_user
58 ldw lr, [\rd+], #\offset + 4 @ load lr_user
59
60 xor \rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE)
61 mov.a asr, \rtemp @ switch back to the PRIV mode
62 .endm
63
64 .macro priv_exit, rpsr
65 mov.a bsr, \rpsr
66 ldm.w (r0 - r15), [sp]+
67 ldm.b (r16 - pc), [sp]+ @ load r0 - pc, asr
68 .endm
69
70 .macro restore_user_regs, fast = 0, offset = 0
71 ldw r1, [sp+], #\offset + S_PSR @ get calling asr
72 ldw lr, [sp+], #\offset + S_PC @ get pc
73 mov.a bsr, r1 @ save in bsr_priv
74 .if \fast
75 add sp, sp, #\offset + S_R1 @ r0 is syscall return value
76 ldm.w (r1 - r15), [sp]+ @ get calling r1 - r15
77 ldur (r16 - lr), [sp]+ @ get calling r16 - lr
78 .else
79 ldm.w (r0 - r15), [sp]+ @ get calling r0 - r15
80 ldur (r16 - lr), [sp]+ @ get calling r16 - lr
81 .endif
82 nop
83 add sp, sp, #S_FRAME_SIZE - S_R16
84 mov.a pc, lr @ return
85 @ and move bsr_priv into asr
86 .endm
87
88 .macro get_thread_info, rd
89 mov \rd, sp >> #13
90 mov \rd, \rd << #13
91 .endm
92
93 .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
94 ldw \base, =(PKUNITY_INTC_BASE)
95 ldw \irqstat, [\base+], #0xC @ INTC_ICIP
96 ldw \tmp, [\base+], #0x4 @ INTC_ICMR
97 and.a \irqstat, \irqstat, \tmp
98 beq 1001f
99 cntlz \irqnr, \irqstat
100 rsub \irqnr, \irqnr, #31
1011001: /* EQ will be set if no irqs pending */
102 .endm
103
104#ifdef CONFIG_DEBUG_LL
105 .macro printreg, reg, temp
106 adr \temp, 901f
107 stm (r0-r3), [\temp]+
108 stw lr, [\temp+], #0x10
109 mov r0, \reg
110 b.l printhex8
111 mov r0, #':'
112 b.l printch
113 mov r0, pc
114 b.l printhex8
115 adr r0, 902f
116 b.l printascii
117 adr \temp, 901f
118 ldm (r0-r3), [\temp]+
119 ldw lr, [\temp+], #0x10
120 b 903f
121901: .word 0, 0, 0, 0, 0 @ r0-r3, lr
122902: .asciz ": epip4d\n"
123 .align
124903:
125 .endm
126#endif
127
128/*
129 * These are the registers used in the syscall handler, and allow us to
130 * have in theory up to 7 arguments to a function - r0 to r6.
131 *
132 * Note that tbl == why is intentional.
133 *
134 * We must set at least "tsk" and "why" when calling ret_with_reschedule.
135 */
136scno .req r21 @ syscall number
137tbl .req r22 @ syscall table pointer
138why .req r22 @ Linux syscall (!= 0)
139tsk .req r23 @ current thread_info
140
141/*
142 * Interrupt handling. Preserves r17, r18, r19
143 */
144 .macro intr_handler
1451: get_irqnr_and_base r0, r6, r5, lr
146 beq 2f
147 mov r1, sp
148 @
149 @ routine called with r0 = irq number, r1 = struct pt_regs *
150 @
151 adr lr, 1b
152 b asm_do_IRQ
1532:
154 .endm
155
156/*
157 * PRIV mode handlers
158 */
159 .macro priv_entry
160 sub sp, sp, #(S_FRAME_SIZE - 4)
161 stm (r1 - r15), [sp]+
162 add r5, sp, #S_R15
163 stm (r16 - r28), [r5]+
164
165 ldm (r1 - r3), [r0]+
166 add r5, sp, #S_SP - 4 @ here for interlock avoidance
167 mov r4, #-1 @ "" "" "" ""
168 add r0, sp, #(S_FRAME_SIZE - 4)
169 stw.w r1, [sp+], #-4 @ save the "real" r0 copied
170 @ from the exception stack
171
172 mov r1, lr
173
174 @
175 @ We are now ready to fill in the remaining blanks on the stack:
176 @
177 @ r0 - sp_priv
178 @ r1 - lr_priv
179 @ r2 - lr_<exception>, already fixed up for correct return/restart
180 @ r3 - bsr_<exception>
181 @ r4 - orig_r0 (see pt_regs definition in ptrace.h)
182 @
183 stm (r0 - r4), [r5]+
184 .endm
185
186/*
187 * User mode handlers
188 *
189 */
190 .macro user_entry
191 sub sp, sp, #S_FRAME_SIZE
192 stm (r1 - r15), [sp+]
193 add r4, sp, #S_R16
194 stm (r16 - r28), [r4]+
195
196 ldm (r1 - r3), [r0]+
197 add r0, sp, #S_PC @ here for interlock avoidance
198 mov r4, #-1 @ "" "" "" ""
199
200 stw r1, [sp] @ save the "real" r0 copied
201 @ from the exception stack
202
203 @
204 @ We are now ready to fill in the remaining blanks on the stack:
205 @
206 @ r2 - lr_<exception>, already fixed up for correct return/restart
207 @ r3 - bsr_<exception>
208 @ r4 - orig_r0 (see pt_regs definition in ptrace.h)
209 @
210 @ Also, separately save sp_user and lr_user
211 @
212 stm (r2 - r4), [r0]+
213 stur (sp, lr), [r0-]
214
215 @
216 @ Enable the alignment trap while in kernel mode
217 @
218 alignment_trap r0
219
220 @
221 @ Clear FP to mark the first stack frame
222 @
223 zero_fp
224 .endm
225
226 .text
227
228@
229@ __invalid - generic code for failed exception
230@ (re-entrant version of handlers)
231@
232__invalid:
233 sub sp, sp, #S_FRAME_SIZE
234 stm (r1 - r15), [sp+]
235 add r1, sp, #S_R16
236 stm (r16 - r28, sp, lr), [r1]+
237
238 zero_fp
239
240 ldm (r4 - r6), [r0]+
241 add r0, sp, #S_PC @ here for interlock avoidance
242 mov r7, #-1 @ "" "" "" ""
243 stw r4, [sp] @ save preserved r0
244 stm (r5 - r7), [r0]+ @ lr_<exception>,
245 @ asr_<exception>, "old_r0"
246
247 mov r0, sp
248 mov r1, asr
249 b bad_mode
250ENDPROC(__invalid)
251
252 .align 5
253__dabt_priv:
254 priv_entry
255
256 @
257 @ get ready to re-enable interrupts if appropriate
258 @
259 mov r17, asr
260 cand.a r3, #PSR_I_BIT
261 bne 1f
262 andn r17, r17, #PSR_I_BIT
2631:
264
265 @
266 @ Call the processor-specific abort handler:
267 @
268 @ r2 - aborted context pc
269 @ r3 - aborted context asr
270 @
271 @ The abort handler must return the aborted address in r0, and
272 @ the fault status register in r1.
273 @
274 movc r1, p0.c3, #0 @ get FSR
275 movc r0, p0.c4, #0 @ get FAR
276
277 @
278 @ set desired INTR state, then call main handler
279 @
280 mov.a asr, r17
281 mov r2, sp
282 b.l do_DataAbort
283
284 @
285 @ INTRs off again before pulling preserved data off the stack
286 @
287 disable_irq r0
288
289 @
290 @ restore BSR and restart the instruction
291 @
292 ldw r2, [sp+], #S_PSR
293 priv_exit r2 @ return from exception
294ENDPROC(__dabt_priv)
295
296 .align 5
297__intr_priv:
298 priv_entry
299
300 intr_handler
301
302 mov r0, #0 @ epip4d
303 movc p0.c5, r0, #14
304 nop; nop; nop; nop; nop; nop; nop; nop
305
306 ldw r4, [sp+], #S_PSR @ irqs are already disabled
307
308 priv_exit r4 @ return from exception
309ENDPROC(__intr_priv)
310
311 .ltorg
312
313 .align 5
314__extn_priv:
315 priv_entry
316
317 mov r0, sp @ struct pt_regs *regs
318 mov r1, asr
319 b bad_mode @ not supported
320ENDPROC(__extn_priv)
321
322 .align 5
323__pabt_priv:
324 priv_entry
325
326 @
327 @ re-enable interrupts if appropriate
328 @
329 mov r17, asr
330 cand.a r3, #PSR_I_BIT
331 bne 1f
332 andn r17, r17, #PSR_I_BIT
3331:
334
335 @
336 @ set args, then call main handler
337 @
338 @ r0 - address of faulting instruction
339 @ r1 - pointer to registers on stack
340 @
341 mov r0, r2 @ pass address of aborted instruction
342 mov r1, #5
343 mov.a asr, r17
344 mov r2, sp @ regs
345 b.l do_PrefetchAbort @ call abort handler
346
347 @
348 @ INTRs off again before pulling preserved data off the stack
349 @
350 disable_irq r0
351
352 @
353 @ restore BSR and restart the instruction
354 @
355 ldw r2, [sp+], #S_PSR
356 priv_exit r2 @ return from exception
357ENDPROC(__pabt_priv)
358
359 .align 5
360.LCcralign:
361 .word cr_alignment
362
363 .align 5
364__dabt_user:
365 user_entry
366
367#ifdef CONFIG_UNICORE_FPU_F64
368 cff ip, s31
369 cand.a ip, #0x08000000 @ FPU execption traps?
370 beq 209f
371
372 ldw ip, [sp+], #S_PC
373 add ip, ip, #4
374 stw ip, [sp+], #S_PC
375 @
376 @ fall through to the emulation code, which returns using r19 if
377 @ it has emulated the instruction, or the more conventional lr
378 @ if we are to treat this as a real extended instruction
379 @
380 @ r0 - instruction
381 @
3821: ldw.u r0, [r2]
383 adr r19, ret_from_exception
384 adr lr, 209f
385 @
386 @ fallthrough to call do_uc_f64
387 @
388/*
389 * Check whether the instruction is a co-processor instruction.
390 * If yes, we need to call the relevant co-processor handler.
391 *
392 * Note that we don't do a full check here for the co-processor
393 * instructions; all instructions with bit 27 set are well
394 * defined. The only instructions that should fault are the
395 * co-processor instructions.
396 *
397 * Emulators may wish to make use of the following registers:
398 * r0 = instruction opcode.
399 * r2 = PC
400 * r19 = normal "successful" return address
401 * r20 = this threads thread_info structure.
402 * lr = unrecognised instruction return address
403 */
404 get_thread_info r20 @ get current thread
405 and r8, r0, #0x00003c00 @ mask out CP number
406 mov r7, #1
407 stb r7, [r20+], #TI_USED_CP + 2 @ set appropriate used_cp[]
408
409 @ F64 hardware support entry point.
410 @ r0 = faulted instruction
411 @ r19 = return address
412 @ r20 = fp_state
413 enable_irq r4
414 add r20, r20, #TI_FPSTATE @ r20 = workspace
415 cff r1, s31 @ get fpu FPSCR
416 andn r2, r1, #0x08000000
417 ctf r2, s31 @ clear 27 bit
418 mov r2, sp @ nothing stacked - regdump is at TOS
419 mov lr, r19 @ setup for a return to the user code
420
421 @ Now call the C code to package up the bounce to the support code
422 @ r0 holds the trigger instruction
423 @ r1 holds the FPSCR value
424 @ r2 pointer to register dump
425 b ucf64_exchandler
426209:
427#endif
428 @
429 @ Call the processor-specific abort handler:
430 @
431 @ r2 - aborted context pc
432 @ r3 - aborted context asr
433 @
434 @ The abort handler must return the aborted address in r0, and
435 @ the fault status register in r1.
436 @
437 movc r1, p0.c3, #0 @ get FSR
438 movc r0, p0.c4, #0 @ get FAR
439
440 @
441 @ INTRs on, then call the main handler
442 @
443 enable_irq r2
444 mov r2, sp
445 adr lr, ret_from_exception
446 b do_DataAbort
447ENDPROC(__dabt_user)
448
449 .align 5
450__intr_user:
451 user_entry
452
453 get_thread_info tsk
454
455 intr_handler
456
457 mov why, #0
458 b ret_to_user
459ENDPROC(__intr_user)
460
461 .ltorg
462
463 .align 5
464__extn_user:
465 user_entry
466
467 mov r0, sp
468 mov r1, asr
469 b bad_mode
470ENDPROC(__extn_user)
471
472 .align 5
473__pabt_user:
474 user_entry
475
476 mov r0, r2 @ pass address of aborted instruction.
477 mov r1, #5
478 enable_irq r1 @ Enable interrupts
479 mov r2, sp @ regs
480 b.l do_PrefetchAbort @ call abort handler
481 /* fall through */
482/*
483 * This is the return code to user mode for abort handlers
484 */
485ENTRY(ret_from_exception)
486 get_thread_info tsk
487 mov why, #0
488 b ret_to_user
489ENDPROC(__pabt_user)
490ENDPROC(ret_from_exception)
491
492/*
493 * Register switch for UniCore V2 processors
494 * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
495 * previous and next are guaranteed not to be the same.
496 */
497ENTRY(__switch_to)
498 add ip, r1, #TI_CPU_SAVE
499 stm.w (r4 - r15), [ip]+
500 stm.w (r16 - r27, sp, lr), [ip]+
501
502#ifdef CONFIG_UNICORE_FPU_F64
503 add ip, r1, #TI_FPSTATE
504 sfm.w (f0 - f7 ), [ip]+
505 sfm.w (f8 - f15), [ip]+
506 sfm.w (f16 - f23), [ip]+
507 sfm.w (f24 - f31), [ip]+
508 cff r4, s31
509 stw r4, [ip]
510
511 add ip, r2, #TI_FPSTATE
512 lfm.w (f0 - f7 ), [ip]+
513 lfm.w (f8 - f15), [ip]+
514 lfm.w (f16 - f23), [ip]+
515 lfm.w (f24 - f31), [ip]+
516 ldw r4, [ip]
517 ctf r4, s31
518#endif
519 add ip, r2, #TI_CPU_SAVE
520 ldm.w (r4 - r15), [ip]+
521 ldm (r16 - r27, sp, pc), [ip]+ @ Load all regs saved previously
522ENDPROC(__switch_to)
523
524 .align 5
525/*
526 * This is the fast syscall return path. We do as little as
527 * possible here, and this includes saving r0 back into the PRIV
528 * stack.
529 */
530ret_fast_syscall:
531 disable_irq r1 @ disable interrupts
532 ldw r1, [tsk+], #TI_FLAGS
533 cand.a r1, #_TIF_WORK_MASK
534 bne fast_work_pending
535
536 @ fast_restore_user_regs
537 restore_user_regs fast = 1, offset = S_OFF
538
539/*
540 * Ok, we need to do extra processing, enter the slow path.
541 */
542fast_work_pending:
543 stw.w r0, [sp+], #S_R0+S_OFF @ returned r0
544work_pending:
545 cand.a r1, #_TIF_NEED_RESCHED
546 bne work_resched
547 cand.a r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME
548 beq no_work_pending
549 mov r0, sp @ 'regs'
550 mov r2, why @ 'syscall'
551 cand.a r1, #_TIF_SIGPENDING @ delivering a signal?
552 cmovne why, #0 @ prevent further restarts
553 b.l do_notify_resume
554 b ret_slow_syscall @ Check work again
555
556work_resched:
557 b.l schedule
558/*
559 * "slow" syscall return path. "why" tells us if this was a real syscall.
560 */
561ENTRY(ret_to_user)
562ret_slow_syscall:
563 disable_irq r1 @ disable interrupts
564 get_thread_info tsk @ epip4d, one path error?!
565 ldw r1, [tsk+], #TI_FLAGS
566 cand.a r1, #_TIF_WORK_MASK
567 bne work_pending
568no_work_pending:
569 @ slow_restore_user_regs
570 restore_user_regs fast = 0, offset = 0
571ENDPROC(ret_to_user)
572
573/*
574 * This is how we return from a fork.
575 */
576ENTRY(ret_from_fork)
577 b.l schedule_tail
578 get_thread_info tsk
579 ldw r1, [tsk+], #TI_FLAGS @ check for syscall tracing
580 mov why, #1
581 cand.a r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
582 beq ret_slow_syscall
583 mov r1, sp
584 mov r0, #1 @ trace exit [IP = 1]
585 b.l syscall_trace
586 b ret_slow_syscall
587ENDPROC(ret_from_fork)
588
589/*=============================================================================
590 * SWI handler
591 *-----------------------------------------------------------------------------
592 */
593 .align 5
594ENTRY(vector_swi)
595 sub sp, sp, #S_FRAME_SIZE
596 stm (r0 - r15), [sp]+ @ Calling r0 - r15
597 add r8, sp, #S_R16
598 stm (r16 - r28), [r8]+ @ Calling r16 - r28
599 add r8, sp, #S_PC
600 stur (sp, lr), [r8-] @ Calling sp, lr
601 mov r8, bsr @ called from non-REAL mode
602 stw lr, [sp+], #S_PC @ Save calling PC
603 stw r8, [sp+], #S_PSR @ Save ASR
604 stw r0, [sp+], #S_OLD_R0 @ Save OLD_R0
605 zero_fp
606
607 /*
608 * Get the system call number.
609 */
610 sub ip, lr, #4
611 ldw.u scno, [ip] @ get SWI instruction
612
613#ifdef CONFIG_ALIGNMENT_TRAP
614 ldw ip, __cr_alignment
615 ldw ip, [ip]
616 movc p0.c1, ip, #0 @ update control register
617#endif
618 enable_irq ip
619
620 get_thread_info tsk
621 ldw tbl, =sys_call_table @ load syscall table pointer
622
623 andn scno, scno, #0xff000000 @ mask off SWI op-code
624 andn scno, scno, #0x00ff0000 @ mask off SWI op-code
625
626 stm.w (r4, r5), [sp-] @ push fifth and sixth args
627 ldw ip, [tsk+], #TI_FLAGS @ check for syscall tracing
628 cand.a ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
629 bne __sys_trace
630
631 csub.a scno, #__NR_syscalls @ check upper syscall limit
632 adr lr, ret_fast_syscall @ return address
633 bea 1f
634 ldw pc, [tbl+], scno << #2 @ call sys_* routine
6351:
636 add r1, sp, #S_OFF
6372: mov why, #0 @ no longer a real syscall
638 b sys_ni_syscall @ not private func
639
640 /*
641 * This is the really slow path. We're going to be doing
642 * context switches, and waiting for our parent to respond.
643 */
644__sys_trace:
645 mov r2, scno
646 add r1, sp, #S_OFF
647 mov r0, #0 @ trace entry [IP = 0]
648 b.l syscall_trace
649
650 adr lr, __sys_trace_return @ return address
651 mov scno, r0 @ syscall number (possibly new)
652 add r1, sp, #S_R0 + S_OFF @ pointer to regs
653 csub.a scno, #__NR_syscalls @ check upper syscall limit
654 bea 2b
655 ldm (r0 - r3), [r1]+ @ have to reload r0 - r3
656 ldw pc, [tbl+], scno << #2 @ call sys_* routine
657
658__sys_trace_return:
659 stw.w r0, [sp+], #S_R0 + S_OFF @ save returned r0
660 mov r2, scno
661 mov r1, sp
662 mov r0, #1 @ trace exit [IP = 1]
663 b.l syscall_trace
664 b ret_slow_syscall
665
666 .align 5
667#ifdef CONFIG_ALIGNMENT_TRAP
668 .type __cr_alignment, #object
669__cr_alignment:
670 .word cr_alignment
671#endif
672 .ltorg
673
674ENTRY(sys_execve)
675 add r3, sp, #S_OFF
676 b __sys_execve
677ENDPROC(sys_execve)
678
679ENTRY(sys_clone)
680 add ip, sp, #S_OFF
681 stw ip, [sp+], #4
682 b __sys_clone
683ENDPROC(sys_clone)
684
685ENTRY(sys_rt_sigreturn)
686 add r0, sp, #S_OFF
687 mov why, #0 @ prevent syscall restart handling
688 b __sys_rt_sigreturn
689ENDPROC(sys_rt_sigreturn)
690
691ENTRY(sys_sigaltstack)
692 ldw r2, [sp+], #S_OFF + S_SP
693 b do_sigaltstack
694ENDPROC(sys_sigaltstack)
695
696 __INIT
697
698/*
699 * Vector stubs.
700 *
701 * This code is copied to 0xffff0200 so we can use branches in the
702 * vectors, rather than ldr's. Note that this code must not
703 * exceed 0x300 bytes.
704 *
705 * Common stub entry macro:
706 * Enter in INTR mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC
707 *
708 * SP points to a minimal amount of processor-private memory, the address
709 * of which is copied into r0 for the mode specific abort handler.
710 */
711 .macro vector_stub, name, mode
712 .align 5
713
714vector_\name:
715 @
716 @ Save r0, lr_<exception> (parent PC) and bsr_<exception>
717 @ (parent ASR)
718 @
719 stw r0, [sp]
720 stw lr, [sp+], #4 @ save r0, lr
721 mov lr, bsr
722 stw lr, [sp+], #8 @ save bsr
723
724 @
725 @ Prepare for PRIV mode. INTRs remain disabled.
726 @
727 mov r0, asr
728 xor r0, r0, #(\mode ^ PRIV_MODE)
729 mov.a bsr, r0
730
731 @
732 @ the branch table must immediately follow this code
733 @
734 and lr, lr, #0x03
735 add lr, lr, #1
736 mov r0, sp
737 ldw lr, [pc+], lr << #2
738 mov.a pc, lr @ branch to handler in PRIV mode
739ENDPROC(vector_\name)
740 .align 2
741 @ handler addresses follow this label
742 .endm
743
744 .globl __stubs_start
745__stubs_start:
746/*
747 * Interrupt dispatcher
748 */
749 vector_stub intr, INTR_MODE
750
751 .long __intr_user @ 0 (USER)
752 .long __invalid @ 1
753 .long __invalid @ 2
754 .long __intr_priv @ 3 (PRIV)
755
756/*
757 * Data abort dispatcher
758 * Enter in ABT mode, bsr = USER ASR, lr = USER PC
759 */
760 vector_stub dabt, ABRT_MODE
761
762 .long __dabt_user @ 0 (USER)
763 .long __invalid @ 1
764 .long __invalid @ 2 (INTR)
765 .long __dabt_priv @ 3 (PRIV)
766
767/*
768 * Prefetch abort dispatcher
769 * Enter in ABT mode, bsr = USER ASR, lr = USER PC
770 */
771 vector_stub pabt, ABRT_MODE
772
773 .long __pabt_user @ 0 (USER)
774 .long __invalid @ 1
775 .long __invalid @ 2 (INTR)
776 .long __pabt_priv @ 3 (PRIV)
777
778/*
779 * Undef instr entry dispatcher
780 * Enter in EXTN mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC
781 */
782 vector_stub extn, EXTN_MODE
783
784 .long __extn_user @ 0 (USER)
785 .long __invalid @ 1
786 .long __invalid @ 2 (INTR)
787 .long __extn_priv @ 3 (PRIV)
788
789/*
790 * We group all the following data together to optimise
791 * for CPUs with separate I & D caches.
792 */
793 .align 5
794
795.LCvswi:
796 .word vector_swi
797
798 .globl __stubs_end
799__stubs_end:
800
801 .equ stubs_offset, __vectors_start + 0x200 - __stubs_start
802
803 .globl __vectors_start
804__vectors_start:
805 jepriv SYS_ERROR0
806 b vector_extn + stubs_offset
807 ldw pc, .LCvswi + stubs_offset
808 b vector_pabt + stubs_offset
809 b vector_dabt + stubs_offset
810 jepriv SYS_ERROR0
811 b vector_intr + stubs_offset
812 jepriv SYS_ERROR0
813
814 .globl __vectors_end
815__vectors_end:
816
817 .data
818
819 .globl cr_alignment
820 .globl cr_no_alignment
821cr_alignment:
822 .space 4
823cr_no_alignment:
824 .space 4
diff --git a/arch/unicore32/kernel/fpu-ucf64.c b/arch/unicore32/kernel/fpu-ucf64.c
new file mode 100644
index 00000000000..282a60ac82b
--- /dev/null
+++ b/arch/unicore32/kernel/fpu-ucf64.c
@@ -0,0 +1,126 @@
1/*
2 * linux/arch/unicore32/kernel/fpu-ucf64.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/types.h>
14#include <linux/kernel.h>
15#include <linux/signal.h>
16#include <linux/sched.h>
17#include <linux/init.h>
18
19#include <asm/fpu-ucf64.h>
20
21/*
22 * A special flag to tell the normalisation code not to normalise.
23 */
24#define F64_NAN_FLAG 0x100
25
26/*
27 * A bit pattern used to indicate the initial (unset) value of the
28 * exception mask, in case nothing handles an instruction. This
29 * doesn't include the NAN flag, which get masked out before
30 * we check for an error.
31 */
32#define F64_EXCEPTION_ERROR ((u32)-1 & ~F64_NAN_FLAG)
33
34/*
35 * Since we aren't building with -mfpu=f64, we need to code
36 * these instructions using their MRC/MCR equivalents.
37 */
38#define f64reg(_f64_) #_f64_
39
40#define cff(_f64_) ({ \
41 u32 __v; \
42 asm("cff %0, " f64reg(_f64_) "@ fmrx %0, " #_f64_ \
43 : "=r" (__v) : : "cc"); \
44 __v; \
45 })
46
47#define ctf(_f64_, _var_) \
48 asm("ctf %0, " f64reg(_f64_) "@ fmxr " #_f64_ ", %0" \
49 : : "r" (_var_) : "cc")
50
51/*
52 * Raise a SIGFPE for the current process.
53 * sicode describes the signal being raised.
54 */
55void ucf64_raise_sigfpe(unsigned int sicode, struct pt_regs *regs)
56{
57 siginfo_t info;
58
59 memset(&info, 0, sizeof(info));
60
61 info.si_signo = SIGFPE;
62 info.si_code = sicode;
63 info.si_addr = (void __user *)(instruction_pointer(regs) - 4);
64
65 /*
66 * This is the same as NWFPE, because it's not clear what
67 * this is used for
68 */
69 current->thread.error_code = 0;
70 current->thread.trap_no = 6;
71
72 send_sig_info(SIGFPE, &info, current);
73}
74
75/*
76 * Handle exceptions of UniCore-F64.
77 */
78void ucf64_exchandler(u32 inst, u32 fpexc, struct pt_regs *regs)
79{
80 u32 tmp = fpexc;
81 u32 exc = F64_EXCEPTION_ERROR & fpexc;
82
83 pr_debug("UniCore-F64: instruction %08x fpscr %08x\n",
84 inst, fpexc);
85
86 if (exc & FPSCR_CMPINSTR_BIT) {
87 if (exc & FPSCR_CON)
88 tmp |= FPSCR_CON;
89 else
90 tmp &= ~(FPSCR_CON);
91 exc &= ~(FPSCR_CMPINSTR_BIT | FPSCR_CON);
92 } else {
93 pr_debug(KERN_ERR "UniCore-F64 Error: unhandled exceptions\n");
94 pr_debug(KERN_ERR "UniCore-F64 FPSCR 0x%08x INST 0x%08x\n",
95 cff(FPSCR), inst);
96
97 ucf64_raise_sigfpe(0, regs);
98 return;
99 }
100
101 /*
102 * Update the FPSCR with the additional exception flags.
103 * Comparison instructions always return at least one of
104 * these flags set.
105 */
106 tmp &= ~(FPSCR_TRAP | FPSCR_IOS | FPSCR_OFS | FPSCR_UFS |
107 FPSCR_IXS | FPSCR_HIS | FPSCR_IOC | FPSCR_OFC |
108 FPSCR_UFC | FPSCR_IXC | FPSCR_HIC);
109
110 tmp |= exc;
111 ctf(FPSCR, tmp);
112}
113
114/*
115 * F64 support code initialisation.
116 */
117static int __init ucf64_init(void)
118{
119 ctf(FPSCR, 0x0); /* FPSCR_UFE | FPSCR_NDE perhaps better */
120
121 printk(KERN_INFO "Enable UniCore-F64 support.\n");
122
123 return 0;
124}
125
126late_initcall(ucf64_init);
diff --git a/arch/unicore32/kernel/gpio.c b/arch/unicore32/kernel/gpio.c
new file mode 100644
index 00000000000..cb12ec39552
--- /dev/null
+++ b/arch/unicore32/kernel/gpio.c
@@ -0,0 +1,122 @@
1/*
2 * linux/arch/unicore32/kernel/gpio.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13/* in FPGA, no GPIO support */
14
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/gpio.h>
18#include <mach/hardware.h>
19
20#ifdef CONFIG_LEDS
21#include <linux/leds.h>
22#include <linux/platform_device.h>
23
24static const struct gpio_led puv3_gpio_leds[] = {
25 { .name = "cpuhealth", .gpio = GPO_CPU_HEALTH, .active_low = 0,
26 .default_trigger = "heartbeat", },
27 { .name = "hdd_led", .gpio = GPO_HDD_LED, .active_low = 1,
28 .default_trigger = "ide-disk", },
29};
30
31static const struct gpio_led_platform_data puv3_gpio_led_data = {
32 .num_leds = ARRAY_SIZE(puv3_gpio_leds),
33 .leds = (void *) puv3_gpio_leds,
34};
35
36static struct platform_device puv3_gpio_gpio_leds = {
37 .name = "leds-gpio",
38 .id = -1,
39 .dev = {
40 .platform_data = (void *) &puv3_gpio_led_data,
41 }
42};
43
44static int __init puv3_gpio_leds_init(void)
45{
46 platform_device_register(&puv3_gpio_gpio_leds);
47 return 0;
48}
49
50device_initcall(puv3_gpio_leds_init);
51#endif
52
53static int puv3_gpio_get(struct gpio_chip *chip, unsigned offset)
54{
55 return readl(GPIO_GPLR) & GPIO_GPIO(offset);
56}
57
58static void puv3_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
59{
60 if (value)
61 writel(GPIO_GPIO(offset), GPIO_GPSR);
62 else
63 writel(GPIO_GPIO(offset), GPIO_GPCR);
64}
65
66static int puv3_direction_input(struct gpio_chip *chip, unsigned offset)
67{
68 unsigned long flags;
69
70 local_irq_save(flags);
71 writel(readl(GPIO_GPDR) & ~GPIO_GPIO(offset), GPIO_GPDR);
72 local_irq_restore(flags);
73 return 0;
74}
75
76static int puv3_direction_output(struct gpio_chip *chip, unsigned offset,
77 int value)
78{
79 unsigned long flags;
80
81 local_irq_save(flags);
82 puv3_gpio_set(chip, offset, value);
83 writel(readl(GPIO_GPDR) | GPIO_GPIO(offset), GPIO_GPDR);
84 local_irq_restore(flags);
85 return 0;
86}
87
88static struct gpio_chip puv3_gpio_chip = {
89 .label = "gpio",
90 .direction_input = puv3_direction_input,
91 .direction_output = puv3_direction_output,
92 .set = puv3_gpio_set,
93 .get = puv3_gpio_get,
94 .base = 0,
95 .ngpio = GPIO_MAX + 1,
96};
97
98void __init puv3_init_gpio(void)
99{
100 writel(GPIO_DIR, GPIO_GPDR);
101#if defined(CONFIG_PUV3_NB0916) || defined(CONFIG_PUV3_SMW0919) \
102 || defined(CONFIG_PUV3_DB0913)
103 gpio_set_value(GPO_WIFI_EN, 1);
104 gpio_set_value(GPO_HDD_LED, 1);
105 gpio_set_value(GPO_VGA_EN, 1);
106 gpio_set_value(GPO_LCD_EN, 1);
107 gpio_set_value(GPO_CAM_PWR_EN, 0);
108 gpio_set_value(GPO_LCD_VCC_EN, 1);
109 gpio_set_value(GPO_SOFT_OFF, 1);
110 gpio_set_value(GPO_BT_EN, 1);
111 gpio_set_value(GPO_FAN_ON, 0);
112 gpio_set_value(GPO_SPKR, 0);
113 gpio_set_value(GPO_CPU_HEALTH, 1);
114 gpio_set_value(GPO_LAN_SEL, 1);
115/*
116 * DO NOT modify the GPO_SET_V1 and GPO_SET_V2 in kernel
117 * gpio_set_value(GPO_SET_V1, 1);
118 * gpio_set_value(GPO_SET_V2, 1);
119 */
120#endif
121 gpiochip_add(&puv3_gpio_chip);
122}
diff --git a/arch/unicore32/kernel/head.S b/arch/unicore32/kernel/head.S
new file mode 100644
index 00000000000..92255f3ab6a
--- /dev/null
+++ b/arch/unicore32/kernel/head.S
@@ -0,0 +1,252 @@
1/*
2 * linux/arch/unicore32/kernel/head.S
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/linkage.h>
13#include <linux/init.h>
14
15#include <asm/assembler.h>
16#include <asm/ptrace.h>
17#include <generated/asm-offsets.h>
18#include <asm/memory.h>
19#include <asm/thread_info.h>
20#include <asm/system.h>
21#include <asm/pgtable-hwdef.h>
22
23#if (PHYS_OFFSET & 0x003fffff)
24#error "PHYS_OFFSET must be at an even 4MiB boundary!"
25#endif
26
27#define KERNEL_RAM_VADDR (PAGE_OFFSET + KERNEL_IMAGE_START)
28#define KERNEL_RAM_PADDR (PHYS_OFFSET + KERNEL_IMAGE_START)
29
30#define KERNEL_PGD_PADDR (KERNEL_RAM_PADDR - 0x1000)
31#define KERNEL_PGD_VADDR (KERNEL_RAM_VADDR - 0x1000)
32
33#define KERNEL_START KERNEL_RAM_VADDR
34#define KERNEL_END _end
35
36/*
37 * swapper_pg_dir is the virtual address of the initial page table.
38 * We place the page tables 4K below KERNEL_RAM_VADDR. Therefore, we must
39 * make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect
40 * the least significant 16 bits to be 0x8000, but we could probably
41 * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x1000.
42 */
43#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
44#error KERNEL_RAM_VADDR must start at 0xXXXX8000
45#endif
46
47 .globl swapper_pg_dir
48 .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x1000
49
50/*
51 * Kernel startup entry point.
52 * ---------------------------
53 *
54 * This is normally called from the decompressor code. The requirements
55 * are: MMU = off, D-cache = off, I-cache = dont care
56 *
57 * This code is mostly position independent, so if you link the kernel at
58 * 0xc0008000, you call this at __pa(0xc0008000).
59 */
60 __HEAD
61ENTRY(stext)
62 @ set asr
63 mov r0, #PRIV_MODE @ ensure priv mode
64 or r0, #PSR_R_BIT | PSR_I_BIT @ disable irqs
65 mov.a asr, r0
66
67 @ process identify
68 movc r0, p0.c0, #0 @ cpuid
69 movl r1, 0xff00ffff @ mask
70 movl r2, 0x4d000863 @ value
71 and r0, r1, r0
72 cxor.a r0, r2
73 bne __error_p @ invalid processor id
74
75 /*
76 * Clear the 4K level 1 swapper page table
77 */
78 movl r0, #KERNEL_PGD_PADDR @ page table address
79 mov r1, #0
80 add r2, r0, #0x1000
81101: stw.w r1, [r0]+, #4
82 stw.w r1, [r0]+, #4
83 stw.w r1, [r0]+, #4
84 stw.w r1, [r0]+, #4
85 cxor.a r0, r2
86 bne 101b
87
88 movl r4, #KERNEL_PGD_PADDR @ page table address
89 mov r7, #PMD_TYPE_SECT | PMD_PRESENT @ page size: section
90 or r7, r7, #PMD_SECT_CACHEABLE @ cacheable
91 or r7, r7, #PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC
92
93 /*
94 * Create identity mapping for first 4MB of kernel to
95 * cater for the MMU enable. This identity mapping
96 * will be removed by paging_init(). We use our current program
97 * counter to determine corresponding section base address.
98 */
99 mov r6, pc
100 mov r6, r6 >> #22 @ start of kernel section
101 or r1, r7, r6 << #22 @ flags + kernel base
102 stw r1, [r4+], r6 << #2 @ identity mapping
103
104 /*
105 * Now setup the pagetables for our kernel direct
106 * mapped region.
107 */
108 add r0, r4, #(KERNEL_START & 0xff000000) >> 20
109 stw.w r1, [r0+], #(KERNEL_START & 0x00c00000) >> 20
110 movl r6, #(KERNEL_END - 1)
111 add r0, r0, #4
112 add r6, r4, r6 >> #20
113102: csub.a r0, r6
114 add r1, r1, #1 << 22
115 bua 103f
116 stw.w r1, [r0]+, #4
117 b 102b
118103:
119 /*
120 * Then map first 4MB of ram in case it contains our boot params.
121 */
122 add r0, r4, #PAGE_OFFSET >> 20
123 or r6, r7, #(PHYS_OFFSET & 0xffc00000)
124 stw r6, [r0]
125
126 ldw r15, __switch_data @ address to jump to after
127
128 /*
129 * Initialise TLB, Caches, and MMU state ready to switch the MMU
130 * on.
131 */
132 mov r0, #0
133 movc p0.c5, r0, #28 @ cache invalidate all
134 nop8
135 movc p0.c6, r0, #6 @ TLB invalidate all
136 nop8
137
138 /*
139 * ..V. .... ..TB IDAM
140 * ..1. .... ..01 1111
141 */
142 movl r0, #0x201f @ control register setting
143
144 /*
145 * Setup common bits before finally enabling the MMU. Essentially
146 * this is just loading the page table pointer and domain access
147 * registers.
148 */
149 #ifndef CONFIG_ALIGNMENT_TRAP
150 andn r0, r0, #CR_A
151 #endif
152 #ifdef CONFIG_CPU_DCACHE_DISABLE
153 andn r0, r0, #CR_D
154 #endif
155 #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
156 andn r0, r0, #CR_B
157 #endif
158 #ifdef CONFIG_CPU_ICACHE_DISABLE
159 andn r0, r0, #CR_I
160 #endif
161
162 movc p0.c2, r4, #0 @ set pgd
163 b __turn_mmu_on
164ENDPROC(stext)
165
166/*
167 * Enable the MMU. This completely changes the stucture of the visible
168 * memory space. You will not be able to trace execution through this.
169 *
170 * r0 = cp#0 control register
171 * r15 = *virtual* address to jump to upon completion
172 */
173 .align 5
174__turn_mmu_on:
175 mov r0, r0
176 movc p0.c1, r0, #0 @ write control reg
177 nop @ fetch inst by phys addr
178 mov pc, r15
179 nop8 @ fetch inst by phys addr
180ENDPROC(__turn_mmu_on)
181
182/*
183 * Setup the initial page tables. We only setup the barest
184 * amount which are required to get the kernel running, which
185 * generally means mapping in the kernel code.
186 *
187 * r9 = cpuid
188 * r10 = procinfo
189 *
190 * Returns:
191 * r0, r3, r6, r7 corrupted
192 * r4 = physical page table address
193 */
194 .ltorg
195
196 .align 2
197 .type __switch_data, %object
198__switch_data:
199 .long __mmap_switched
200 .long __bss_start @ r6
201 .long _end @ r7
202 .long cr_alignment @ r8
203 .long init_thread_union + THREAD_START_SP @ sp
204
205/*
206 * The following fragment of code is executed with the MMU on in MMU mode,
207 * and uses absolute addresses; this is not position independent.
208 *
209 * r0 = cp#0 control register
210 */
211__mmap_switched:
212 adr r3, __switch_data + 4
213
214 ldm.w (r6, r7, r8), [r3]+
215 ldw sp, [r3]
216
217 mov fp, #0 @ Clear BSS (and zero fp)
218203: csub.a r6, r7
219 bea 204f
220 stw.w fp, [r6]+,#4
221 b 203b
222204:
223 andn r1, r0, #CR_A @ Clear 'A' bit
224 stm (r0, r1), [r8]+ @ Save control register values
225 b start_kernel
226ENDPROC(__mmap_switched)
227
228/*
229 * Exception handling. Something went wrong and we can't proceed. We
230 * ought to tell the user, but since we don't have any guarantee that
231 * we're even running on the right architecture, we do virtually nothing.
232 *
233 * If CONFIG_DEBUG_LL is set we try to print out something about the error
234 * and hope for the best (useful if bootloader fails to pass a proper
235 * machine ID for example).
236 */
237__error_p:
238#ifdef CONFIG_DEBUG_LL
239 adr r0, str_p1
240 b.l printascii
241 mov r0, r9
242 b.l printhex8
243 adr r0, str_p2
244 b.l printascii
245901: nop8
246 b 901b
247str_p1: .asciz "\nError: unrecognized processor variant (0x"
248str_p2: .asciz ").\n"
249 .align
250#endif
251ENDPROC(__error_p)
252
diff --git a/arch/unicore32/kernel/hibernate.c b/arch/unicore32/kernel/hibernate.c
new file mode 100644
index 00000000000..7d0f0b7983a
--- /dev/null
+++ b/arch/unicore32/kernel/hibernate.c
@@ -0,0 +1,160 @@
1/*
2 * linux/arch/unicore32/kernel/hibernate.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/gfp.h>
15#include <linux/suspend.h>
16#include <linux/bootmem.h>
17
18#include <asm/system.h>
19#include <asm/page.h>
20#include <asm/pgtable.h>
21#include <asm/pgalloc.h>
22#include <asm/suspend.h>
23
24#include "mach/pm.h"
25
26/* Pointer to the temporary resume page tables */
27pgd_t *resume_pg_dir;
28
29struct swsusp_arch_regs swsusp_arch_regs_cpu0;
30
31/*
32 * Create a middle page table on a resume-safe page and put a pointer to it in
33 * the given global directory entry. This only returns the gd entry
34 * in non-PAE compilation mode, since the middle layer is folded.
35 */
36static pmd_t *resume_one_md_table_init(pgd_t *pgd)
37{
38 pud_t *pud;
39 pmd_t *pmd_table;
40
41 pud = pud_offset(pgd, 0);
42 pmd_table = pmd_offset(pud, 0);
43
44 return pmd_table;
45}
46
47/*
48 * Create a page table on a resume-safe page and place a pointer to it in
49 * a middle page directory entry.
50 */
51static pte_t *resume_one_page_table_init(pmd_t *pmd)
52{
53 if (pmd_none(*pmd)) {
54 pte_t *page_table = (pte_t *)get_safe_page(GFP_ATOMIC);
55 if (!page_table)
56 return NULL;
57
58 set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_KERNEL_TABLE));
59
60 BUG_ON(page_table != pte_offset_kernel(pmd, 0));
61
62 return page_table;
63 }
64
65 return pte_offset_kernel(pmd, 0);
66}
67
68/*
69 * This maps the physical memory to kernel virtual address space, a total
70 * of max_low_pfn pages, by creating page tables starting from address
71 * PAGE_OFFSET. The page tables are allocated out of resume-safe pages.
72 */
73static int resume_physical_mapping_init(pgd_t *pgd_base)
74{
75 unsigned long pfn;
76 pgd_t *pgd;
77 pmd_t *pmd;
78 pte_t *pte;
79 int pgd_idx, pmd_idx;
80
81 pgd_idx = pgd_index(PAGE_OFFSET);
82 pgd = pgd_base + pgd_idx;
83 pfn = 0;
84
85 for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
86 pmd = resume_one_md_table_init(pgd);
87 if (!pmd)
88 return -ENOMEM;
89
90 if (pfn >= max_low_pfn)
91 continue;
92
93 for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD; pmd++, pmd_idx++) {
94 pte_t *max_pte;
95
96 if (pfn >= max_low_pfn)
97 break;
98
99 /* Map with normal page tables.
100 * NOTE: We can mark everything as executable here
101 */
102 pte = resume_one_page_table_init(pmd);
103 if (!pte)
104 return -ENOMEM;
105
106 max_pte = pte + PTRS_PER_PTE;
107 for (; pte < max_pte; pte++, pfn++) {
108 if (pfn >= max_low_pfn)
109 break;
110
111 set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
112 }
113 }
114 }
115
116 return 0;
117}
118
119static inline void resume_init_first_level_page_table(pgd_t *pg_dir)
120{
121}
122
123int swsusp_arch_resume(void)
124{
125 int error;
126
127 resume_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC);
128 if (!resume_pg_dir)
129 return -ENOMEM;
130
131 resume_init_first_level_page_table(resume_pg_dir);
132 error = resume_physical_mapping_init(resume_pg_dir);
133 if (error)
134 return error;
135
136 /* We have got enough memory and from now on we cannot recover */
137 restore_image(resume_pg_dir, restore_pblist);
138 return 0;
139}
140
141/*
142 * pfn_is_nosave - check if given pfn is in the 'nosave' section
143 */
144
145int pfn_is_nosave(unsigned long pfn)
146{
147 unsigned long begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
148 unsigned long end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT;
149
150 return (pfn >= begin_pfn) && (pfn < end_pfn);
151}
152
153void save_processor_state(void)
154{
155}
156
157void restore_processor_state(void)
158{
159 local_flush_tlb_all();
160}
diff --git a/arch/unicore32/kernel/hibernate_asm.S b/arch/unicore32/kernel/hibernate_asm.S
new file mode 100644
index 00000000000..cc3c65253c8
--- /dev/null
+++ b/arch/unicore32/kernel/hibernate_asm.S
@@ -0,0 +1,117 @@
1/*
2 * linux/arch/unicore32/kernel/hibernate_asm.S
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/sys.h>
15#include <linux/errno.h>
16#include <linux/linkage.h>
17#include <generated/asm-offsets.h>
18#include <asm/page.h>
19#include <asm/pgtable.h>
20#include <asm/assembler.h>
21
22@ restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist)
23@ r0: resume_pg_dir
24@ r1: restore_pblist
25@ copy restore_pblist pages
26@ restore registers from swsusp_arch_regs_cpu0
27@
28ENTRY(restore_image)
29 sub r0, r0, #PAGE_OFFSET
30 mov r5, #0
31 movc p0.c6, r5, #6 @invalidate ITLB & DTLB
32 movc p0.c2, r0, #0
33 nop
34 nop
35 nop
36 nop
37 nop
38 nop
39 nop
40
41 .p2align 4,,7
42101:
43 csub.a r1, #0
44 beq 109f
45
46 ldw r6, [r1+], #PBE_ADDRESS
47 ldw r7, [r1+], #PBE_ORIN_ADDRESS
48
49 movl ip, #128
50102: ldm.w (r8 - r15), [r6]+
51 stm.w (r8 - r15), [r7]+
52 sub.a ip, ip, #1
53 bne 102b
54
55 ldw r1, [r1+], #PBE_NEXT
56 b 101b
57
58 .p2align 4,,7
59109:
60 /* go back to the original page tables */
61 ldw r0, =swapper_pg_dir
62 sub r0, r0, #PAGE_OFFSET
63 mov r5, #0
64 movc p0.c6, r5, #6
65 movc p0.c2, r0, #0
66 nop
67 nop
68 nop
69 nop
70 nop
71 nop
72 nop
73
74#ifdef CONFIG_UNICORE_FPU_F64
75 ldw ip, 1f
76 add ip, ip, #SWSUSP_FPSTATE
77 lfm.w (f0 - f7 ), [ip]+
78 lfm.w (f8 - f15), [ip]+
79 lfm.w (f16 - f23), [ip]+
80 lfm.w (f24 - f31), [ip]+
81 ldw r4, [ip]
82 ctf r4, s31
83#endif
84 mov r0, #0x0
85 ldw ip, 1f
86 add ip, ip, #SWSUSP_CPU
87 ldm.w (r4 - r15), [ip]+
88 ldm (r16 - r27, sp, pc), [ip]+ @ Load all regs saved previously
89
90 .align 2
911: .long swsusp_arch_regs_cpu0
92
93
94@ swsusp_arch_suspend()
95@ - prepare pc for resume, return from function without swsusp_save on resume
96@ - save registers in swsusp_arch_regs_cpu0
97@ - call swsusp_save write suspend image
98
99ENTRY(swsusp_arch_suspend)
100 ldw ip, 1f
101 add ip, ip, #SWSUSP_CPU
102 stm.w (r4 - r15), [ip]+
103 stm.w (r16 - r27, sp, lr), [ip]+
104
105#ifdef CONFIG_UNICORE_FPU_F64
106 ldw ip, 1f
107 add ip, ip, #SWSUSP_FPSTATE
108 sfm.w (f0 - f7 ), [ip]+
109 sfm.w (f8 - f15), [ip]+
110 sfm.w (f16 - f23), [ip]+
111 sfm.w (f24 - f31), [ip]+
112 cff r4, s31
113 stw r4, [ip]
114#endif
115 b swsusp_save @ no return
116
1171: .long swsusp_arch_regs_cpu0
diff --git a/arch/unicore32/kernel/init_task.c b/arch/unicore32/kernel/init_task.c
new file mode 100644
index 00000000000..a35a1e50e4f
--- /dev/null
+++ b/arch/unicore32/kernel/init_task.c
@@ -0,0 +1,44 @@
1/*
2 * linux/arch/unicore32/kernel/init_task.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/mm.h>
13#include <linux/module.h>
14#include <linux/fs.h>
15#include <linux/sched.h>
16#include <linux/init.h>
17#include <linux/init_task.h>
18#include <linux/mqueue.h>
19#include <linux/uaccess.h>
20
21#include <asm/pgtable.h>
22
23static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
24static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
25/*
26 * Initial thread structure.
27 *
28 * We need to make sure that this is 8192-byte aligned due to the
29 * way process stacks are handled. This is done by making sure
30 * the linker maps this in the .text segment right after head.S,
31 * and making head.S ensure the proper alignment.
32 *
33 * The things we do for performance..
34 */
35union thread_union init_thread_union __init_task_data = {
36 INIT_THREAD_INFO(init_task) };
37
38/*
39 * Initial task structure.
40 *
41 * All other task structs will be allocated on slabs in fork.c
42 */
43struct task_struct init_task = INIT_TASK(init_task);
44EXPORT_SYMBOL(init_task);
diff --git a/arch/unicore32/kernel/irq.c b/arch/unicore32/kernel/irq.c
new file mode 100644
index 00000000000..b23624cf306
--- /dev/null
+++ b/arch/unicore32/kernel/irq.c
@@ -0,0 +1,426 @@
1/*
2 * linux/arch/unicore32/kernel/irq.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/kernel_stat.h>
13#include <linux/module.h>
14#include <linux/signal.h>
15#include <linux/ioport.h>
16#include <linux/interrupt.h>
17#include <linux/irq.h>
18#include <linux/random.h>
19#include <linux/smp.h>
20#include <linux/init.h>
21#include <linux/seq_file.h>
22#include <linux/errno.h>
23#include <linux/list.h>
24#include <linux/kallsyms.h>
25#include <linux/proc_fs.h>
26#include <linux/sysdev.h>
27#include <linux/gpio.h>
28
29#include <asm/system.h>
30#include <mach/hardware.h>
31
32#include "setup.h"
33
34/*
35 * PKUnity GPIO edge detection for IRQs:
36 * IRQs are generated on Falling-Edge, Rising-Edge, or both.
37 * Use this instead of directly setting GRER/GFER.
38 */
39static int GPIO_IRQ_rising_edge;
40static int GPIO_IRQ_falling_edge;
41static int GPIO_IRQ_mask = 0;
42
43#define GPIO_MASK(irq) (1 << (irq - IRQ_GPIO0))
44
45static int puv3_gpio_type(struct irq_data *d, unsigned int type)
46{
47 unsigned int mask;
48
49 if (d->irq < IRQ_GPIOHIGH)
50 mask = 1 << d->irq;
51 else
52 mask = GPIO_MASK(d->irq);
53
54 if (type == IRQ_TYPE_PROBE) {
55 if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask)
56 return 0;
57 type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
58 }
59
60 if (type & IRQ_TYPE_EDGE_RISING)
61 GPIO_IRQ_rising_edge |= mask;
62 else
63 GPIO_IRQ_rising_edge &= ~mask;
64 if (type & IRQ_TYPE_EDGE_FALLING)
65 GPIO_IRQ_falling_edge |= mask;
66 else
67 GPIO_IRQ_falling_edge &= ~mask;
68
69 writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER);
70 writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER);
71
72 return 0;
73}
74
75/*
76 * GPIO IRQs must be acknowledged. This is for IRQs from 0 to 7.
77 */
78static void puv3_low_gpio_ack(struct irq_data *d)
79{
80 writel((1 << d->irq), GPIO_GEDR);
81}
82
83static void puv3_low_gpio_mask(struct irq_data *d)
84{
85 writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR);
86}
87
88static void puv3_low_gpio_unmask(struct irq_data *d)
89{
90 writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR);
91}
92
93static int puv3_low_gpio_wake(struct irq_data *d, unsigned int on)
94{
95 if (on)
96 writel(readl(PM_PWER) | (1 << d->irq), PM_PWER);
97 else
98 writel(readl(PM_PWER) & ~(1 << d->irq), PM_PWER);
99 return 0;
100}
101
102static struct irq_chip puv3_low_gpio_chip = {
103 .name = "GPIO-low",
104 .irq_ack = puv3_low_gpio_ack,
105 .irq_mask = puv3_low_gpio_mask,
106 .irq_unmask = puv3_low_gpio_unmask,
107 .irq_set_type = puv3_gpio_type,
108 .irq_set_wake = puv3_low_gpio_wake,
109};
110
111/*
112 * IRQ8 (GPIO0 through 27) handler. We enter here with the
113 * irq_controller_lock held, and IRQs disabled. Decode the IRQ
114 * and call the handler.
115 */
116static void
117puv3_gpio_handler(unsigned int irq, struct irq_desc *desc)
118{
119 unsigned int mask;
120
121 mask = readl(GPIO_GEDR);
122 do {
123 /*
124 * clear down all currently active IRQ sources.
125 * We will be processing them all.
126 */
127 writel(mask, GPIO_GEDR);
128
129 irq = IRQ_GPIO0;
130 do {
131 if (mask & 1)
132 generic_handle_irq(irq);
133 mask >>= 1;
134 irq++;
135 } while (mask);
136 mask = readl(GPIO_GEDR);
137 } while (mask);
138}
139
140/*
141 * GPIO0-27 edge IRQs need to be handled specially.
142 * In addition, the IRQs are all collected up into one bit in the
143 * interrupt controller registers.
144 */
145static void puv3_high_gpio_ack(struct irq_data *d)
146{
147 unsigned int mask = GPIO_MASK(d->irq);
148
149 writel(mask, GPIO_GEDR);
150}
151
152static void puv3_high_gpio_mask(struct irq_data *d)
153{
154 unsigned int mask = GPIO_MASK(d->irq);
155
156 GPIO_IRQ_mask &= ~mask;
157
158 writel(readl(GPIO_GRER) & ~mask, GPIO_GRER);
159 writel(readl(GPIO_GFER) & ~mask, GPIO_GFER);
160}
161
162static void puv3_high_gpio_unmask(struct irq_data *d)
163{
164 unsigned int mask = GPIO_MASK(d->irq);
165
166 GPIO_IRQ_mask |= mask;
167
168 writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER);
169 writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER);
170}
171
172static int puv3_high_gpio_wake(struct irq_data *d, unsigned int on)
173{
174 if (on)
175 writel(readl(PM_PWER) | PM_PWER_GPIOHIGH, PM_PWER);
176 else
177 writel(readl(PM_PWER) & ~PM_PWER_GPIOHIGH, PM_PWER);
178 return 0;
179}
180
181static struct irq_chip puv3_high_gpio_chip = {
182 .name = "GPIO-high",
183 .irq_ack = puv3_high_gpio_ack,
184 .irq_mask = puv3_high_gpio_mask,
185 .irq_unmask = puv3_high_gpio_unmask,
186 .irq_set_type = puv3_gpio_type,
187 .irq_set_wake = puv3_high_gpio_wake,
188};
189
190/*
191 * We don't need to ACK IRQs on the PKUnity unless they're GPIOs
192 * this is for internal IRQs i.e. from 8 to 31.
193 */
194static void puv3_mask_irq(struct irq_data *d)
195{
196 writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR);
197}
198
199static void puv3_unmask_irq(struct irq_data *d)
200{
201 writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR);
202}
203
204/*
205 * Apart form GPIOs, only the RTC alarm can be a wakeup event.
206 */
207static int puv3_set_wake(struct irq_data *d, unsigned int on)
208{
209 if (d->irq == IRQ_RTCAlarm) {
210 if (on)
211 writel(readl(PM_PWER) | PM_PWER_RTC, PM_PWER);
212 else
213 writel(readl(PM_PWER) & ~PM_PWER_RTC, PM_PWER);
214 return 0;
215 }
216 return -EINVAL;
217}
218
219static struct irq_chip puv3_normal_chip = {
220 .name = "PKUnity-v3",
221 .irq_ack = puv3_mask_irq,
222 .irq_mask = puv3_mask_irq,
223 .irq_unmask = puv3_unmask_irq,
224 .irq_set_wake = puv3_set_wake,
225};
226
227static struct resource irq_resource = {
228 .name = "irqs",
229 .start = io_v2p(PKUNITY_INTC_BASE),
230 .end = io_v2p(PKUNITY_INTC_BASE) + 0xFFFFF,
231};
232
233static struct puv3_irq_state {
234 unsigned int saved;
235 unsigned int icmr;
236 unsigned int iclr;
237 unsigned int iccr;
238} puv3_irq_state;
239
240static int puv3_irq_suspend(struct sys_device *dev, pm_message_t state)
241{
242 struct puv3_irq_state *st = &puv3_irq_state;
243
244 st->saved = 1;
245 st->icmr = readl(INTC_ICMR);
246 st->iclr = readl(INTC_ICLR);
247 st->iccr = readl(INTC_ICCR);
248
249 /*
250 * Disable all GPIO-based interrupts.
251 */
252 writel(readl(INTC_ICMR) & ~(0x1ff), INTC_ICMR);
253
254 /*
255 * Set the appropriate edges for wakeup.
256 */
257 writel(readl(PM_PWER) & GPIO_IRQ_rising_edge, GPIO_GRER);
258 writel(readl(PM_PWER) & GPIO_IRQ_falling_edge, GPIO_GFER);
259
260 /*
261 * Clear any pending GPIO interrupts.
262 */
263 writel(readl(GPIO_GEDR), GPIO_GEDR);
264
265 return 0;
266}
267
268static int puv3_irq_resume(struct sys_device *dev)
269{
270 struct puv3_irq_state *st = &puv3_irq_state;
271
272 if (st->saved) {
273 writel(st->iccr, INTC_ICCR);
274 writel(st->iclr, INTC_ICLR);
275
276 writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER);
277 writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER);
278
279 writel(st->icmr, INTC_ICMR);
280 }
281 return 0;
282}
283
284static struct sysdev_class puv3_irq_sysclass = {
285 .name = "pkunity-irq",
286 .suspend = puv3_irq_suspend,
287 .resume = puv3_irq_resume,
288};
289
290static struct sys_device puv3_irq_device = {
291 .id = 0,
292 .cls = &puv3_irq_sysclass,
293};
294
295static int __init puv3_irq_init_devicefs(void)
296{
297 sysdev_class_register(&puv3_irq_sysclass);
298 return sysdev_register(&puv3_irq_device);
299}
300
301device_initcall(puv3_irq_init_devicefs);
302
303void __init init_IRQ(void)
304{
305 unsigned int irq;
306
307 request_resource(&iomem_resource, &irq_resource);
308
309 /* disable all IRQs */
310 writel(0, INTC_ICMR);
311
312 /* all IRQs are IRQ, not REAL */
313 writel(0, INTC_ICLR);
314
315 /* clear all GPIO edge detects */
316 writel(FMASK(8, 0) & ~FIELD(1, 1, GPI_SOFF_REQ), GPIO_GPIR);
317 writel(0, GPIO_GFER);
318 writel(0, GPIO_GRER);
319 writel(0x0FFFFFFF, GPIO_GEDR);
320
321 writel(1, INTC_ICCR);
322
323 for (irq = 0; irq < IRQ_GPIOHIGH; irq++) {
324 set_irq_chip(irq, &puv3_low_gpio_chip);
325 set_irq_handler(irq, handle_edge_irq);
326 irq_modify_status(irq,
327 IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN,
328 0);
329 }
330
331 for (irq = IRQ_GPIOHIGH + 1; irq < IRQ_GPIO0; irq++) {
332 set_irq_chip(irq, &puv3_normal_chip);
333 set_irq_handler(irq, handle_level_irq);
334 irq_modify_status(irq,
335 IRQ_NOREQUEST | IRQ_NOAUTOEN,
336 IRQ_NOPROBE);
337 }
338
339 for (irq = IRQ_GPIO0; irq <= IRQ_GPIO27; irq++) {
340 set_irq_chip(irq, &puv3_high_gpio_chip);
341 set_irq_handler(irq, handle_edge_irq);
342 irq_modify_status(irq,
343 IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN,
344 0);
345 }
346
347 /*
348 * Install handler for GPIO 0-27 edge detect interrupts
349 */
350 set_irq_chip(IRQ_GPIOHIGH, &puv3_normal_chip);
351 set_irq_chained_handler(IRQ_GPIOHIGH, puv3_gpio_handler);
352
353#ifdef CONFIG_PUV3_GPIO
354 puv3_init_gpio();
355#endif
356}
357
358int show_interrupts(struct seq_file *p, void *v)
359{
360 int i = *(loff_t *) v, cpu;
361 struct irq_desc *desc;
362 struct irqaction *action;
363 unsigned long flags;
364
365 if (i == 0) {
366 char cpuname[12];
367
368 seq_printf(p, " ");
369 for_each_present_cpu(cpu) {
370 sprintf(cpuname, "CPU%d", cpu);
371 seq_printf(p, " %10s", cpuname);
372 }
373 seq_putc(p, '\n');
374 }
375
376 if (i < nr_irqs) {
377 desc = irq_to_desc(i);
378 raw_spin_lock_irqsave(&desc->lock, flags);
379 action = desc->action;
380 if (!action)
381 goto unlock;
382
383 seq_printf(p, "%3d: ", i);
384 for_each_present_cpu(cpu)
385 seq_printf(p, "%10u ", kstat_irqs_cpu(i, cpu));
386 seq_printf(p, " %10s", desc->irq_data.chip->name ? : "-");
387 seq_printf(p, " %s", action->name);
388 for (action = action->next; action; action = action->next)
389 seq_printf(p, ", %s", action->name);
390
391 seq_putc(p, '\n');
392unlock:
393 raw_spin_unlock_irqrestore(&desc->lock, flags);
394 } else if (i == nr_irqs) {
395 seq_printf(p, "Error in interrupt!\n");
396 }
397 return 0;
398}
399
400/*
401 * do_IRQ handles all hardware IRQ's. Decoded IRQs should not
402 * come via this function. Instead, they should provide their
403 * own 'handler'
404 */
405asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
406{
407 struct pt_regs *old_regs = set_irq_regs(regs);
408
409 irq_enter();
410
411 /*
412 * Some hardware gives randomly wrong interrupts. Rather
413 * than crashing, do something sensible.
414 */
415 if (unlikely(irq >= nr_irqs)) {
416 if (printk_ratelimit())
417 printk(KERN_WARNING "Bad IRQ%u\n", irq);
418 ack_bad_irq(irq);
419 } else {
420 generic_handle_irq(irq);
421 }
422
423 irq_exit();
424 set_irq_regs(old_regs);
425}
426
diff --git a/arch/unicore32/kernel/ksyms.c b/arch/unicore32/kernel/ksyms.c
new file mode 100644
index 00000000000..a8970809428
--- /dev/null
+++ b/arch/unicore32/kernel/ksyms.c
@@ -0,0 +1,99 @@
1/*
2 * linux/arch/unicore32/kernel/ksyms.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/sched.h>
14#include <linux/string.h>
15#include <linux/cryptohash.h>
16#include <linux/delay.h>
17#include <linux/in6.h>
18#include <linux/syscalls.h>
19#include <linux/uaccess.h>
20#include <linux/io.h>
21
22#include <asm/checksum.h>
23#include <asm/system.h>
24
25#include "ksyms.h"
26
27EXPORT_SYMBOL(__uc32_find_next_zero_bit);
28EXPORT_SYMBOL(__uc32_find_next_bit);
29
30EXPORT_SYMBOL(__backtrace);
31
32 /* platform dependent support */
33EXPORT_SYMBOL(__udelay);
34EXPORT_SYMBOL(__const_udelay);
35
36 /* networking */
37EXPORT_SYMBOL(csum_partial);
38EXPORT_SYMBOL(csum_partial_copy_from_user);
39EXPORT_SYMBOL(csum_partial_copy_nocheck);
40EXPORT_SYMBOL(__csum_ipv6_magic);
41
42 /* io */
43#ifndef __raw_readsb
44EXPORT_SYMBOL(__raw_readsb);
45#endif
46#ifndef __raw_readsw
47EXPORT_SYMBOL(__raw_readsw);
48#endif
49#ifndef __raw_readsl
50EXPORT_SYMBOL(__raw_readsl);
51#endif
52#ifndef __raw_writesb
53EXPORT_SYMBOL(__raw_writesb);
54#endif
55#ifndef __raw_writesw
56EXPORT_SYMBOL(__raw_writesw);
57#endif
58#ifndef __raw_writesl
59EXPORT_SYMBOL(__raw_writesl);
60#endif
61
62 /* string / mem functions */
63EXPORT_SYMBOL(strchr);
64EXPORT_SYMBOL(strrchr);
65EXPORT_SYMBOL(memset);
66EXPORT_SYMBOL(memcpy);
67EXPORT_SYMBOL(memmove);
68EXPORT_SYMBOL(memchr);
69
70 /* user mem (segment) */
71EXPORT_SYMBOL(__strnlen_user);
72EXPORT_SYMBOL(__strncpy_from_user);
73
74EXPORT_SYMBOL(copy_page);
75
76EXPORT_SYMBOL(__copy_from_user);
77EXPORT_SYMBOL(__copy_to_user);
78EXPORT_SYMBOL(__clear_user);
79
80EXPORT_SYMBOL(__get_user_1);
81EXPORT_SYMBOL(__get_user_2);
82EXPORT_SYMBOL(__get_user_4);
83
84EXPORT_SYMBOL(__put_user_1);
85EXPORT_SYMBOL(__put_user_2);
86EXPORT_SYMBOL(__put_user_4);
87EXPORT_SYMBOL(__put_user_8);
88
89EXPORT_SYMBOL(__ashldi3);
90EXPORT_SYMBOL(__ashrdi3);
91EXPORT_SYMBOL(__divsi3);
92EXPORT_SYMBOL(__lshrdi3);
93EXPORT_SYMBOL(__modsi3);
94EXPORT_SYMBOL(__muldi3);
95EXPORT_SYMBOL(__ucmpdi2);
96EXPORT_SYMBOL(__udivsi3);
97EXPORT_SYMBOL(__umodsi3);
98EXPORT_SYMBOL(__bswapsi2);
99
diff --git a/arch/unicore32/kernel/ksyms.h b/arch/unicore32/kernel/ksyms.h
new file mode 100644
index 00000000000..185cdc712d0
--- /dev/null
+++ b/arch/unicore32/kernel/ksyms.h
@@ -0,0 +1,15 @@
1/*
2 * libgcc functions - functions that are used internally by the
3 * compiler... (prototypes are not correct though, but that
4 * doesn't really matter since they're not versioned).
5 */
6extern void __ashldi3(void);
7extern void __ashrdi3(void);
8extern void __divsi3(void);
9extern void __lshrdi3(void);
10extern void __modsi3(void);
11extern void __muldi3(void);
12extern void __ucmpdi2(void);
13extern void __udivsi3(void);
14extern void __umodsi3(void);
15extern void __bswapsi2(void);
diff --git a/arch/unicore32/kernel/module.c b/arch/unicore32/kernel/module.c
new file mode 100644
index 00000000000..3e5a38d71a1
--- /dev/null
+++ b/arch/unicore32/kernel/module.c
@@ -0,0 +1,152 @@
1/*
2 * linux/arch/unicore32/kernel/module.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/moduleloader.h>
14#include <linux/kernel.h>
15#include <linux/mm.h>
16#include <linux/elf.h>
17#include <linux/vmalloc.h>
18#include <linux/fs.h>
19#include <linux/string.h>
20#include <linux/gfp.h>
21
22#include <asm/pgtable.h>
23#include <asm/sections.h>
24
25void *module_alloc(unsigned long size)
26{
27 struct vm_struct *area;
28
29 size = PAGE_ALIGN(size);
30 if (!size)
31 return NULL;
32
33 area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
34 if (!area)
35 return NULL;
36
37 return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC);
38}
39
40void module_free(struct module *module, void *region)
41{
42 vfree(region);
43}
44
45int module_frob_arch_sections(Elf_Ehdr *hdr,
46 Elf_Shdr *sechdrs,
47 char *secstrings,
48 struct module *mod)
49{
50 return 0;
51}
52
53int
54apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
55 unsigned int relindex, struct module *module)
56{
57 Elf32_Shdr *symsec = sechdrs + symindex;
58 Elf32_Shdr *relsec = sechdrs + relindex;
59 Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
60 Elf32_Rel *rel = (void *)relsec->sh_addr;
61 unsigned int i;
62
63 for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) {
64 unsigned long loc;
65 Elf32_Sym *sym;
66 s32 offset;
67
68 offset = ELF32_R_SYM(rel->r_info);
69 if (offset < 0 || offset >
70 (symsec->sh_size / sizeof(Elf32_Sym))) {
71 printk(KERN_ERR "%s: bad relocation, "
72 "section %d reloc %d\n",
73 module->name, relindex, i);
74 return -ENOEXEC;
75 }
76
77 sym = ((Elf32_Sym *)symsec->sh_addr) + offset;
78
79 if (rel->r_offset < 0 || rel->r_offset >
80 dstsec->sh_size - sizeof(u32)) {
81 printk(KERN_ERR "%s: out of bounds relocation, "
82 "section %d reloc %d offset %d size %d\n",
83 module->name, relindex, i, rel->r_offset,
84 dstsec->sh_size);
85 return -ENOEXEC;
86 }
87
88 loc = dstsec->sh_addr + rel->r_offset;
89
90 switch (ELF32_R_TYPE(rel->r_info)) {
91 case R_UNICORE_NONE:
92 /* ignore */
93 break;
94
95 case R_UNICORE_ABS32:
96 *(u32 *)loc += sym->st_value;
97 break;
98
99 case R_UNICORE_PC24:
100 case R_UNICORE_CALL:
101 case R_UNICORE_JUMP24:
102 offset = (*(u32 *)loc & 0x00ffffff) << 2;
103 if (offset & 0x02000000)
104 offset -= 0x04000000;
105
106 offset += sym->st_value - loc;
107 if (offset & 3 ||
108 offset <= (s32)0xfe000000 ||
109 offset >= (s32)0x02000000) {
110 printk(KERN_ERR
111 "%s: relocation out of range, section "
112 "%d reloc %d sym '%s'\n", module->name,
113 relindex, i, strtab + sym->st_name);
114 return -ENOEXEC;
115 }
116
117 offset >>= 2;
118
119 *(u32 *)loc &= 0xff000000;
120 *(u32 *)loc |= offset & 0x00ffffff;
121 break;
122
123 default:
124 printk(KERN_ERR "%s: unknown relocation: %u\n",
125 module->name, ELF32_R_TYPE(rel->r_info));
126 return -ENOEXEC;
127 }
128 }
129 return 0;
130}
131
132int
133apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
134 unsigned int symindex, unsigned int relsec,
135 struct module *module)
136{
137 printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
138 module->name);
139 return -ENOEXEC;
140}
141
142int
143module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
144 struct module *module)
145{
146 return 0;
147}
148
149void
150module_arch_cleanup(struct module *mod)
151{
152}
diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c
new file mode 100644
index 00000000000..100eab842e6
--- /dev/null
+++ b/arch/unicore32/kernel/pci.c
@@ -0,0 +1,404 @@
1/*
2 * linux/arch/unicore32/kernel/pci.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * PCI bios-type initialisation for PCI machines
13 *
14 */
15#include <linux/module.h>
16#include <linux/kernel.h>
17#include <linux/interrupt.h>
18#include <linux/pci.h>
19#include <linux/slab.h>
20#include <linux/init.h>
21#include <linux/io.h>
22
23static int debug_pci;
24static int use_firmware;
25
26#define CONFIG_CMD(bus, devfn, where) \
27 (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
28
29static int
30puv3_read_config(struct pci_bus *bus, unsigned int devfn, int where,
31 int size, u32 *value)
32{
33 writel(CONFIG_CMD(bus, devfn, where), PCICFG_ADDR);
34 switch (size) {
35 case 1:
36 *value = (readl(PCICFG_DATA) >> ((where & 3) * 8)) & 0xFF;
37 break;
38 case 2:
39 *value = (readl(PCICFG_DATA) >> ((where & 2) * 8)) & 0xFFFF;
40 break;
41 case 4:
42 *value = readl(PCICFG_DATA);
43 break;
44 }
45 return PCIBIOS_SUCCESSFUL;
46}
47
48static int
49puv3_write_config(struct pci_bus *bus, unsigned int devfn, int where,
50 int size, u32 value)
51{
52 writel(CONFIG_CMD(bus, devfn, where), PCICFG_ADDR);
53 switch (size) {
54 case 1:
55 writel((readl(PCICFG_DATA) & ~FMASK(8, (where&3)*8))
56 | FIELD(value, 8, (where&3)*8), PCICFG_DATA);
57 break;
58 case 2:
59 writel((readl(PCICFG_DATA) & ~FMASK(16, (where&2)*8))
60 | FIELD(value, 16, (where&2)*8), PCICFG_DATA);
61 break;
62 case 4:
63 writel(value, PCICFG_DATA);
64 break;
65 }
66 return PCIBIOS_SUCCESSFUL;
67}
68
69struct pci_ops pci_puv3_ops = {
70 .read = puv3_read_config,
71 .write = puv3_write_config,
72};
73
74void pci_puv3_preinit(void)
75{
76 printk(KERN_DEBUG "PCI: PKUnity PCI Controller Initializing ...\n");
77 /* config PCI bridge base */
78 writel(io_v2p(PKUNITY_PCIBRI_BASE), PCICFG_BRIBASE);
79
80 writel(0, PCIBRI_AHBCTL0);
81 writel(io_v2p(PKUNITY_PCIBRI_BASE) | PCIBRI_BARx_MEM, PCIBRI_AHBBAR0);
82 writel(0xFFFF0000, PCIBRI_AHBAMR0);
83 writel(0, PCIBRI_AHBTAR0);
84
85 writel(PCIBRI_CTLx_AT, PCIBRI_AHBCTL1);
86 writel(io_v2p(PKUNITY_PCILIO_BASE) | PCIBRI_BARx_IO, PCIBRI_AHBBAR1);
87 writel(0xFFFF0000, PCIBRI_AHBAMR1);
88 writel(0x00000000, PCIBRI_AHBTAR1);
89
90 writel(PCIBRI_CTLx_PREF, PCIBRI_AHBCTL2);
91 writel(io_v2p(PKUNITY_PCIMEM_BASE) | PCIBRI_BARx_MEM, PCIBRI_AHBBAR2);
92 writel(0xF8000000, PCIBRI_AHBAMR2);
93 writel(0, PCIBRI_AHBTAR2);
94
95 writel(io_v2p(PKUNITY_PCIAHB_BASE) | PCIBRI_BARx_MEM, PCIBRI_BAR1);
96
97 writel(PCIBRI_CTLx_AT | PCIBRI_CTLx_PREF, PCIBRI_PCICTL0);
98 writel(io_v2p(PKUNITY_PCIAHB_BASE) | PCIBRI_BARx_MEM, PCIBRI_PCIBAR0);
99 writel(0xF8000000, PCIBRI_PCIAMR0);
100 writel(PKUNITY_SDRAM_BASE, PCIBRI_PCITAR0);
101
102 writel(readl(PCIBRI_CMD) | PCIBRI_CMD_IO | PCIBRI_CMD_MEM, PCIBRI_CMD);
103}
104
105static int __init pci_puv3_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
106{
107 if (dev->bus->number == 0) {
108#ifdef CONFIG_ARCH_FPGA /* 4 pci slots */
109 if (dev->devfn == 0x00)
110 return IRQ_PCIINTA;
111 else if (dev->devfn == 0x08)
112 return IRQ_PCIINTB;
113 else if (dev->devfn == 0x10)
114 return IRQ_PCIINTC;
115 else if (dev->devfn == 0x18)
116 return IRQ_PCIINTD;
117#endif
118#ifdef CONFIG_PUV3_DB0913 /* 3 pci slots */
119 if (dev->devfn == 0x30)
120 return IRQ_PCIINTB;
121 else if (dev->devfn == 0x60)
122 return IRQ_PCIINTC;
123 else if (dev->devfn == 0x58)
124 return IRQ_PCIINTD;
125#endif
126#if defined(CONFIG_PUV3_NB0916) || defined(CONFIG_PUV3_SMW0919)
127 /* only support 2 pci devices */
128 if (dev->devfn == 0x00)
129 return IRQ_PCIINTC; /* sata */
130#endif
131 }
132 return -1;
133}
134
135/*
136 * Only first 128MB of memory can be accessed via PCI.
137 * We use GFP_DMA to allocate safe buffers to do map/unmap.
138 * This is really ugly and we need a better way of specifying
139 * DMA-capable regions of memory.
140 */
141void __init puv3_pci_adjust_zones(unsigned long *zone_size,
142 unsigned long *zhole_size)
143{
144 unsigned int sz = SZ_128M >> PAGE_SHIFT;
145
146 /*
147 * Only adjust if > 128M on current system
148 */
149 if (zone_size[0] <= sz)
150 return;
151
152 zone_size[1] = zone_size[0] - sz;
153 zone_size[0] = sz;
154 zhole_size[1] = zhole_size[0];
155 zhole_size[0] = 0;
156}
157
158void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
159{
160 if (debug_pci)
161 printk(KERN_DEBUG "PCI: Assigning IRQ %02d to %s\n",
162 irq, pci_name(dev));
163 pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
164}
165
166/*
167 * If the bus contains any of these devices, then we must not turn on
168 * parity checking of any kind.
169 */
170static inline int pdev_bad_for_parity(struct pci_dev *dev)
171{
172 return 0;
173}
174
175/*
176 * pcibios_fixup_bus - Called after each bus is probed,
177 * but before its children are examined.
178 */
179void __devinit pcibios_fixup_bus(struct pci_bus *bus)
180{
181 struct pci_dev *dev;
182 u16 features = PCI_COMMAND_SERR
183 | PCI_COMMAND_PARITY
184 | PCI_COMMAND_FAST_BACK;
185
186 bus->resource[0] = &ioport_resource;
187 bus->resource[1] = &iomem_resource;
188
189 /*
190 * Walk the devices on this bus, working out what we can
191 * and can't support.
192 */
193 list_for_each_entry(dev, &bus->devices, bus_list) {
194 u16 status;
195
196 pci_read_config_word(dev, PCI_STATUS, &status);
197
198 /*
199 * If any device on this bus does not support fast back
200 * to back transfers, then the bus as a whole is not able
201 * to support them. Having fast back to back transfers
202 * on saves us one PCI cycle per transaction.
203 */
204 if (!(status & PCI_STATUS_FAST_BACK))
205 features &= ~PCI_COMMAND_FAST_BACK;
206
207 if (pdev_bad_for_parity(dev))
208 features &= ~(PCI_COMMAND_SERR
209 | PCI_COMMAND_PARITY);
210
211 switch (dev->class >> 8) {
212 case PCI_CLASS_BRIDGE_PCI:
213 pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status);
214 status |= PCI_BRIDGE_CTL_PARITY
215 | PCI_BRIDGE_CTL_MASTER_ABORT;
216 status &= ~(PCI_BRIDGE_CTL_BUS_RESET
217 | PCI_BRIDGE_CTL_FAST_BACK);
218 pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status);
219 break;
220
221 case PCI_CLASS_BRIDGE_CARDBUS:
222 pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL,
223 &status);
224 status |= PCI_CB_BRIDGE_CTL_PARITY
225 | PCI_CB_BRIDGE_CTL_MASTER_ABORT;
226 pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL,
227 status);
228 break;
229 }
230 }
231
232 /*
233 * Now walk the devices again, this time setting them up.
234 */
235 list_for_each_entry(dev, &bus->devices, bus_list) {
236 u16 cmd;
237
238 pci_read_config_word(dev, PCI_COMMAND, &cmd);
239 cmd |= features;
240 pci_write_config_word(dev, PCI_COMMAND, cmd);
241
242 pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
243 L1_CACHE_BYTES >> 2);
244 }
245
246 /*
247 * Propagate the flags to the PCI bridge.
248 */
249 if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
250 if (features & PCI_COMMAND_FAST_BACK)
251 bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK;
252 if (features & PCI_COMMAND_PARITY)
253 bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY;
254 }
255
256 /*
257 * Report what we did for this bus
258 */
259 printk(KERN_INFO "PCI: bus%d: Fast back to back transfers %sabled\n",
260 bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis");
261}
262#ifdef CONFIG_HOTPLUG
263EXPORT_SYMBOL(pcibios_fixup_bus);
264#endif
265
266static int __init pci_common_init(void)
267{
268 struct pci_bus *puv3_bus;
269
270 pci_puv3_preinit();
271
272 puv3_bus = pci_scan_bus(0, &pci_puv3_ops, NULL);
273
274 if (!puv3_bus)
275 panic("PCI: unable to scan bus!");
276
277 pci_fixup_irqs(pci_common_swizzle, pci_puv3_map_irq);
278
279 if (!use_firmware) {
280 /*
281 * Size the bridge windows.
282 */
283 pci_bus_size_bridges(puv3_bus);
284
285 /*
286 * Assign resources.
287 */
288 pci_bus_assign_resources(puv3_bus);
289 }
290
291 /*
292 * Tell drivers about devices found.
293 */
294 pci_bus_add_devices(puv3_bus);
295
296 return 0;
297}
298subsys_initcall(pci_common_init);
299
300char * __devinit pcibios_setup(char *str)
301{
302 if (!strcmp(str, "debug")) {
303 debug_pci = 1;
304 return NULL;
305 } else if (!strcmp(str, "firmware")) {
306 use_firmware = 1;
307 return NULL;
308 }
309 return str;
310}
311
312/*
313 * From arch/i386/kernel/pci-i386.c:
314 *
315 * We need to avoid collisions with `mirrored' VGA ports
316 * and other strange ISA hardware, so we always want the
317 * addresses to be allocated in the 0x000-0x0ff region
318 * modulo 0x400.
319 *
320 * Why? Because some silly external IO cards only decode
321 * the low 10 bits of the IO address. The 0x00-0xff region
322 * is reserved for motherboard devices that decode all 16
323 * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
324 * but we want to try to avoid allocating at 0x2900-0x2bff
325 * which might be mirrored at 0x0100-0x03ff..
326 */
327resource_size_t pcibios_align_resource(void *data, const struct resource *res,
328 resource_size_t size, resource_size_t align)
329{
330 resource_size_t start = res->start;
331
332 if (res->flags & IORESOURCE_IO && start & 0x300)
333 start = (start + 0x3ff) & ~0x3ff;
334
335 start = (start + align - 1) & ~(align - 1);
336
337 return start;
338}
339
340/**
341 * pcibios_enable_device - Enable I/O and memory.
342 * @dev: PCI device to be enabled
343 */
344int pcibios_enable_device(struct pci_dev *dev, int mask)
345{
346 u16 cmd, old_cmd;
347 int idx;
348 struct resource *r;
349
350 pci_read_config_word(dev, PCI_COMMAND, &cmd);
351 old_cmd = cmd;
352 for (idx = 0; idx < 6; idx++) {
353 /* Only set up the requested stuff */
354 if (!(mask & (1 << idx)))
355 continue;
356
357 r = dev->resource + idx;
358 if (!r->start && r->end) {
359 printk(KERN_ERR "PCI: Device %s not available because"
360 " of resource collisions\n", pci_name(dev));
361 return -EINVAL;
362 }
363 if (r->flags & IORESOURCE_IO)
364 cmd |= PCI_COMMAND_IO;
365 if (r->flags & IORESOURCE_MEM)
366 cmd |= PCI_COMMAND_MEMORY;
367 }
368
369 /*
370 * Bridges (eg, cardbus bridges) need to be fully enabled
371 */
372 if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
373 cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
374
375 if (cmd != old_cmd) {
376 printk("PCI: enabling device %s (%04x -> %04x)\n",
377 pci_name(dev), old_cmd, cmd);
378 pci_write_config_word(dev, PCI_COMMAND, cmd);
379 }
380 return 0;
381}
382
383int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
384 enum pci_mmap_state mmap_state, int write_combine)
385{
386 unsigned long phys;
387
388 if (mmap_state == pci_mmap_io)
389 return -EINVAL;
390
391 phys = vma->vm_pgoff;
392
393 /*
394 * Mark this as IO
395 */
396 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
397
398 if (remap_pfn_range(vma, vma->vm_start, phys,
399 vma->vm_end - vma->vm_start,
400 vma->vm_page_prot))
401 return -EAGAIN;
402
403 return 0;
404}
diff --git a/arch/unicore32/kernel/pm.c b/arch/unicore32/kernel/pm.c
new file mode 100644
index 00000000000..784bc2db3b2
--- /dev/null
+++ b/arch/unicore32/kernel/pm.c
@@ -0,0 +1,123 @@
1/*
2 * linux/arch/unicore32/kernel/pm.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/suspend.h>
16#include <linux/errno.h>
17#include <linux/slab.h>
18#include <linux/io.h>
19
20#include <mach/hardware.h>
21#include <mach/pm.h>
22
23#include "setup.h"
24
25struct puv3_cpu_pm_fns *puv3_cpu_pm_fns;
26static unsigned long *sleep_save;
27
28int puv3_pm_enter(suspend_state_t state)
29{
30 unsigned long sleep_save_checksum = 0, checksum = 0;
31 int i;
32
33 /* skip registers saving for standby */
34 if (state != PM_SUSPEND_STANDBY) {
35 puv3_cpu_pm_fns->save(sleep_save);
36 /* before sleeping, calculate and save a checksum */
37 for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++)
38 sleep_save_checksum += sleep_save[i];
39 }
40
41 /* *** go zzz *** */
42 puv3_cpu_pm_fns->enter(state);
43 cpu_init();
44#ifdef CONFIG_INPUT_KEYBOARD
45 puv3_ps2_init();
46#endif
47#ifdef CONFIG_PCI
48 pci_puv3_preinit();
49#endif
50 if (state != PM_SUSPEND_STANDBY) {
51 /* after sleeping, validate the checksum */
52 for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++)
53 checksum += sleep_save[i];
54
55 /* if invalid, display message and wait for a hardware reset */
56 if (checksum != sleep_save_checksum) {
57 while (1)
58 puv3_cpu_pm_fns->enter(state);
59 }
60 puv3_cpu_pm_fns->restore(sleep_save);
61 }
62
63 pr_debug("*** made it back from resume\n");
64
65 return 0;
66}
67EXPORT_SYMBOL_GPL(puv3_pm_enter);
68
69unsigned long sleep_phys_sp(void *sp)
70{
71 return virt_to_phys(sp);
72}
73
74static int puv3_pm_valid(suspend_state_t state)
75{
76 if (puv3_cpu_pm_fns)
77 return puv3_cpu_pm_fns->valid(state);
78
79 return -EINVAL;
80}
81
82static int puv3_pm_prepare(void)
83{
84 int ret = 0;
85
86 if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->prepare)
87 ret = puv3_cpu_pm_fns->prepare();
88
89 return ret;
90}
91
92static void puv3_pm_finish(void)
93{
94 if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->finish)
95 puv3_cpu_pm_fns->finish();
96}
97
98static struct platform_suspend_ops puv3_pm_ops = {
99 .valid = puv3_pm_valid,
100 .enter = puv3_pm_enter,
101 .prepare = puv3_pm_prepare,
102 .finish = puv3_pm_finish,
103};
104
105static int __init puv3_pm_init(void)
106{
107 if (!puv3_cpu_pm_fns) {
108 printk(KERN_ERR "no valid puv3_cpu_pm_fns defined\n");
109 return -EINVAL;
110 }
111
112 sleep_save = kmalloc(puv3_cpu_pm_fns->save_count
113 * sizeof(unsigned long), GFP_KERNEL);
114 if (!sleep_save) {
115 printk(KERN_ERR "failed to alloc memory for pm save\n");
116 return -ENOMEM;
117 }
118
119 suspend_set_ops(&puv3_pm_ops);
120 return 0;
121}
122
123device_initcall(puv3_pm_init);
diff --git a/arch/unicore32/kernel/process.c b/arch/unicore32/kernel/process.c
new file mode 100644
index 00000000000..ba401df971e
--- /dev/null
+++ b/arch/unicore32/kernel/process.c
@@ -0,0 +1,389 @@
1/*
2 * linux/arch/unicore32/kernel/process.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <stdarg.h>
13
14#include <linux/module.h>
15#include <linux/sched.h>
16#include <linux/kernel.h>
17#include <linux/mm.h>
18#include <linux/stddef.h>
19#include <linux/unistd.h>
20#include <linux/delay.h>
21#include <linux/reboot.h>
22#include <linux/interrupt.h>
23#include <linux/kallsyms.h>
24#include <linux/init.h>
25#include <linux/cpu.h>
26#include <linux/elfcore.h>
27#include <linux/pm.h>
28#include <linux/tick.h>
29#include <linux/utsname.h>
30#include <linux/uaccess.h>
31#include <linux/random.h>
32#include <linux/gpio.h>
33#include <linux/stacktrace.h>
34
35#include <asm/cacheflush.h>
36#include <asm/processor.h>
37#include <asm/system.h>
38#include <asm/stacktrace.h>
39
40#include "setup.h"
41
42static const char * const processor_modes[] = {
43 "UK00", "UK01", "UK02", "UK03", "UK04", "UK05", "UK06", "UK07",
44 "UK08", "UK09", "UK0A", "UK0B", "UK0C", "UK0D", "UK0E", "UK0F",
45 "USER", "REAL", "INTR", "PRIV", "UK14", "UK15", "UK16", "ABRT",
46 "UK18", "UK19", "UK1A", "EXTN", "UK1C", "UK1D", "UK1E", "SUSR"
47};
48
49/*
50 * The idle thread, has rather strange semantics for calling pm_idle,
51 * but this is what x86 does and we need to do the same, so that
52 * things like cpuidle get called in the same way.
53 */
54void cpu_idle(void)
55{
56 /* endless idle loop with no priority at all */
57 while (1) {
58 tick_nohz_stop_sched_tick(1);
59 while (!need_resched()) {
60 local_irq_disable();
61 stop_critical_timings();
62 cpu_do_idle();
63 local_irq_enable();
64 start_critical_timings();
65 }
66 tick_nohz_restart_sched_tick();
67 preempt_enable_no_resched();
68 schedule();
69 preempt_disable();
70 }
71}
72
73static char reboot_mode = 'h';
74
75int __init reboot_setup(char *str)
76{
77 reboot_mode = str[0];
78 return 1;
79}
80
81__setup("reboot=", reboot_setup);
82
83void machine_halt(void)
84{
85 gpio_set_value(GPO_SOFT_OFF, 0);
86}
87
88/*
89 * Function pointers to optional machine specific functions
90 */
91void (*pm_power_off)(void) = NULL;
92
93void machine_power_off(void)
94{
95 if (pm_power_off)
96 pm_power_off();
97 machine_halt();
98}
99
100void machine_restart(char *cmd)
101{
102 /* Disable interrupts first */
103 local_irq_disable();
104
105 /*
106 * Tell the mm system that we are going to reboot -
107 * we may need it to insert some 1:1 mappings so that
108 * soft boot works.
109 */
110 setup_mm_for_reboot(reboot_mode);
111
112 /* Clean and invalidate caches */
113 flush_cache_all();
114
115 /* Turn off caching */
116 cpu_proc_fin();
117
118 /* Push out any further dirty data, and ensure cache is empty */
119 flush_cache_all();
120
121 /*
122 * Now handle reboot code.
123 */
124 if (reboot_mode == 's') {
125 /* Jump into ROM at address 0xffff0000 */
126 cpu_reset(VECTORS_BASE);
127 } else {
128 writel(0x00002001, PM_PLLSYSCFG); /* cpu clk = 250M */
129 writel(0x00100800, PM_PLLDDRCFG); /* ddr clk = 44M */
130 writel(0x00002001, PM_PLLVGACFG); /* vga clk = 250M */
131
132 /* Use on-chip reset capability */
133 /* following instructions must be in one icache line */
134 __asm__ __volatile__(
135 " .align 5\n\t"
136 " stw %1, [%0]\n\t"
137 "201: ldw r0, [%0]\n\t"
138 " cmpsub.a r0, #0\n\t"
139 " bne 201b\n\t"
140 " stw %3, [%2]\n\t"
141 " nop; nop; nop\n\t"
142 /* prefetch 3 instructions at most */
143 :
144 : "r" (PM_PMCR),
145 "r" (PM_PMCR_CFBSYS | PM_PMCR_CFBDDR
146 | PM_PMCR_CFBVGA),
147 "r" (RESETC_SWRR),
148 "r" (RESETC_SWRR_SRB)
149 : "r0", "memory");
150 }
151
152 /*
153 * Whoops - the architecture was unable to reboot.
154 * Tell the user!
155 */
156 mdelay(1000);
157 printk(KERN_EMERG "Reboot failed -- System halted\n");
158 do { } while (1);
159}
160
161void __show_regs(struct pt_regs *regs)
162{
163 unsigned long flags;
164 char buf[64];
165
166 printk(KERN_DEFAULT "CPU: %d %s (%s %.*s)\n",
167 raw_smp_processor_id(), print_tainted(),
168 init_utsname()->release,
169 (int)strcspn(init_utsname()->version, " "),
170 init_utsname()->version);
171 print_symbol("PC is at %s\n", instruction_pointer(regs));
172 print_symbol("LR is at %s\n", regs->UCreg_lr);
173 printk(KERN_DEFAULT "pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n"
174 "sp : %08lx ip : %08lx fp : %08lx\n",
175 regs->UCreg_pc, regs->UCreg_lr, regs->UCreg_asr,
176 regs->UCreg_sp, regs->UCreg_ip, regs->UCreg_fp);
177 printk(KERN_DEFAULT "r26: %08lx r25: %08lx r24: %08lx\n",
178 regs->UCreg_26, regs->UCreg_25,
179 regs->UCreg_24);
180 printk(KERN_DEFAULT "r23: %08lx r22: %08lx r21: %08lx r20: %08lx\n",
181 regs->UCreg_23, regs->UCreg_22,
182 regs->UCreg_21, regs->UCreg_20);
183 printk(KERN_DEFAULT "r19: %08lx r18: %08lx r17: %08lx r16: %08lx\n",
184 regs->UCreg_19, regs->UCreg_18,
185 regs->UCreg_17, regs->UCreg_16);
186 printk(KERN_DEFAULT "r15: %08lx r14: %08lx r13: %08lx r12: %08lx\n",
187 regs->UCreg_15, regs->UCreg_14,
188 regs->UCreg_13, regs->UCreg_12);
189 printk(KERN_DEFAULT "r11: %08lx r10: %08lx r9 : %08lx r8 : %08lx\n",
190 regs->UCreg_11, regs->UCreg_10,
191 regs->UCreg_09, regs->UCreg_08);
192 printk(KERN_DEFAULT "r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
193 regs->UCreg_07, regs->UCreg_06,
194 regs->UCreg_05, regs->UCreg_04);
195 printk(KERN_DEFAULT "r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
196 regs->UCreg_03, regs->UCreg_02,
197 regs->UCreg_01, regs->UCreg_00);
198
199 flags = regs->UCreg_asr;
200 buf[0] = flags & PSR_S_BIT ? 'S' : 's';
201 buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
202 buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
203 buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
204 buf[4] = '\0';
205
206 printk(KERN_DEFAULT "Flags: %s INTR o%s REAL o%s Mode %s Segment %s\n",
207 buf, interrupts_enabled(regs) ? "n" : "ff",
208 fast_interrupts_enabled(regs) ? "n" : "ff",
209 processor_modes[processor_mode(regs)],
210 segment_eq(get_fs(), get_ds()) ? "kernel" : "user");
211 {
212 unsigned int ctrl;
213
214 buf[0] = '\0';
215 {
216 unsigned int transbase;
217 asm("movc %0, p0.c2, #0\n"
218 : "=r" (transbase));
219 snprintf(buf, sizeof(buf), " Table: %08x", transbase);
220 }
221 asm("movc %0, p0.c1, #0\n" : "=r" (ctrl));
222
223 printk(KERN_DEFAULT "Control: %08x%s\n", ctrl, buf);
224 }
225}
226
227void show_regs(struct pt_regs *regs)
228{
229 printk(KERN_DEFAULT "\n");
230 printk(KERN_DEFAULT "Pid: %d, comm: %20s\n",
231 task_pid_nr(current), current->comm);
232 __show_regs(regs);
233 __backtrace();
234}
235
236/*
237 * Free current thread data structures etc..
238 */
239void exit_thread(void)
240{
241}
242
243void flush_thread(void)
244{
245 struct thread_info *thread = current_thread_info();
246 struct task_struct *tsk = current;
247
248 memset(thread->used_cp, 0, sizeof(thread->used_cp));
249 memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
250#ifdef CONFIG_UNICORE_FPU_F64
251 memset(&thread->fpstate, 0, sizeof(struct fp_state));
252#endif
253}
254
255void release_thread(struct task_struct *dead_task)
256{
257}
258
259asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
260
261int
262copy_thread(unsigned long clone_flags, unsigned long stack_start,
263 unsigned long stk_sz, struct task_struct *p, struct pt_regs *regs)
264{
265 struct thread_info *thread = task_thread_info(p);
266 struct pt_regs *childregs = task_pt_regs(p);
267
268 *childregs = *regs;
269 childregs->UCreg_00 = 0;
270 childregs->UCreg_sp = stack_start;
271
272 memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
273 thread->cpu_context.sp = (unsigned long)childregs;
274 thread->cpu_context.pc = (unsigned long)ret_from_fork;
275
276 if (clone_flags & CLONE_SETTLS)
277 childregs->UCreg_16 = regs->UCreg_03;
278
279 return 0;
280}
281
282/*
283 * Fill in the task's elfregs structure for a core dump.
284 */
285int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs)
286{
287 elf_core_copy_regs(elfregs, task_pt_regs(t));
288 return 1;
289}
290
291/*
292 * fill in the fpe structure for a core dump...
293 */
294int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fp)
295{
296 struct thread_info *thread = current_thread_info();
297 int used_math = thread->used_cp[1] | thread->used_cp[2];
298
299#ifdef CONFIG_UNICORE_FPU_F64
300 if (used_math)
301 memcpy(fp, &thread->fpstate, sizeof(*fp));
302#endif
303 return used_math != 0;
304}
305EXPORT_SYMBOL(dump_fpu);
306
307/*
308 * Shuffle the argument into the correct register before calling the
309 * thread function. r1 is the thread argument, r2 is the pointer to
310 * the thread function, and r3 points to the exit function.
311 */
312asm(".pushsection .text\n"
313" .align\n"
314" .type kernel_thread_helper, #function\n"
315"kernel_thread_helper:\n"
316" mov.a asr, r7\n"
317" mov r0, r4\n"
318" mov lr, r6\n"
319" mov pc, r5\n"
320" .size kernel_thread_helper, . - kernel_thread_helper\n"
321" .popsection");
322
323/*
324 * Create a kernel thread.
325 */
326pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
327{
328 struct pt_regs regs;
329
330 memset(&regs, 0, sizeof(regs));
331
332 regs.UCreg_04 = (unsigned long)arg;
333 regs.UCreg_05 = (unsigned long)fn;
334 regs.UCreg_06 = (unsigned long)do_exit;
335 regs.UCreg_07 = PRIV_MODE;
336 regs.UCreg_pc = (unsigned long)kernel_thread_helper;
337 regs.UCreg_asr = regs.UCreg_07 | PSR_I_BIT;
338
339 return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
340}
341EXPORT_SYMBOL(kernel_thread);
342
343unsigned long get_wchan(struct task_struct *p)
344{
345 struct stackframe frame;
346 int count = 0;
347 if (!p || p == current || p->state == TASK_RUNNING)
348 return 0;
349
350 frame.fp = thread_saved_fp(p);
351 frame.sp = thread_saved_sp(p);
352 frame.lr = 0; /* recovered from the stack */
353 frame.pc = thread_saved_pc(p);
354 do {
355 int ret = unwind_frame(&frame);
356 if (ret < 0)
357 return 0;
358 if (!in_sched_functions(frame.pc))
359 return frame.pc;
360 } while ((count++) < 16);
361 return 0;
362}
363
364unsigned long arch_randomize_brk(struct mm_struct *mm)
365{
366 unsigned long range_end = mm->brk + 0x02000000;
367 return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
368}
369
370/*
371 * The vectors page is always readable from user space for the
372 * atomic helpers and the signal restart code. Let's declare a mapping
373 * for it so it is visible through ptrace and /proc/<pid>/mem.
374 */
375
376int vectors_user_mapping(void)
377{
378 struct mm_struct *mm = current->mm;
379 return install_special_mapping(mm, 0xffff0000, PAGE_SIZE,
380 VM_READ | VM_EXEC |
381 VM_MAYREAD | VM_MAYEXEC |
382 VM_ALWAYSDUMP | VM_RESERVED,
383 NULL);
384}
385
386const char *arch_vma_name(struct vm_area_struct *vma)
387{
388 return (vma->vm_start == 0xffff0000) ? "[vectors]" : NULL;
389}
diff --git a/arch/unicore32/kernel/ptrace.c b/arch/unicore32/kernel/ptrace.c
new file mode 100644
index 00000000000..9f07c08da05
--- /dev/null
+++ b/arch/unicore32/kernel/ptrace.c
@@ -0,0 +1,149 @@
1/*
2 * linux/arch/unicore32/kernel/ptrace.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * By Ross Biro 1/23/92
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14#include <linux/kernel.h>
15#include <linux/ptrace.h>
16#include <linux/signal.h>
17#include <linux/uaccess.h>
18
19/*
20 * this routine will get a word off of the processes privileged stack.
21 * the offset is how far from the base addr as stored in the THREAD.
22 * this routine assumes that all the privileged stacks are in our
23 * data space.
24 */
25static inline long get_user_reg(struct task_struct *task, int offset)
26{
27 return task_pt_regs(task)->uregs[offset];
28}
29
30/*
31 * this routine will put a word on the processes privileged stack.
32 * the offset is how far from the base addr as stored in the THREAD.
33 * this routine assumes that all the privileged stacks are in our
34 * data space.
35 */
36static inline int
37put_user_reg(struct task_struct *task, int offset, long data)
38{
39 struct pt_regs newregs, *regs = task_pt_regs(task);
40 int ret = -EINVAL;
41
42 newregs = *regs;
43 newregs.uregs[offset] = data;
44
45 if (valid_user_regs(&newregs)) {
46 regs->uregs[offset] = data;
47 ret = 0;
48 }
49
50 return ret;
51}
52
53/*
54 * Called by kernel/ptrace.c when detaching..
55 */
56void ptrace_disable(struct task_struct *child)
57{
58}
59
60/*
61 * We actually access the pt_regs stored on the kernel stack.
62 */
63static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
64 unsigned long __user *ret)
65{
66 unsigned long tmp;
67
68 tmp = 0;
69 if (off < sizeof(struct pt_regs))
70 tmp = get_user_reg(tsk, off >> 2);
71
72 return put_user(tmp, ret);
73}
74
75/*
76 * We actually access the pt_regs stored on the kernel stack.
77 */
78static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
79 unsigned long val)
80{
81 if (off >= sizeof(struct pt_regs))
82 return 0;
83
84 return put_user_reg(tsk, off >> 2, val);
85}
86
87long arch_ptrace(struct task_struct *child, long request,
88 unsigned long addr, unsigned long data)
89{
90 int ret;
91 unsigned long __user *datap = (unsigned long __user *) data;
92
93 switch (request) {
94 case PTRACE_PEEKUSR:
95 ret = ptrace_read_user(child, addr, datap);
96 break;
97
98 case PTRACE_POKEUSR:
99 ret = ptrace_write_user(child, addr, data);
100 break;
101
102 case PTRACE_GET_THREAD_AREA:
103 ret = put_user(task_pt_regs(child)->UCreg_16,
104 datap);
105 break;
106
107 default:
108 ret = ptrace_request(child, request, addr, data);
109 break;
110 }
111
112 return ret;
113}
114
115asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
116{
117 unsigned long ip;
118
119 if (!test_thread_flag(TIF_SYSCALL_TRACE))
120 return scno;
121 if (!(current->ptrace & PT_PTRACED))
122 return scno;
123
124 /*
125 * Save IP. IP is used to denote syscall entry/exit:
126 * IP = 0 -> entry, = 1 -> exit
127 */
128 ip = regs->UCreg_ip;
129 regs->UCreg_ip = why;
130
131 current_thread_info()->syscall = scno;
132
133 /* the 0x80 provides a way for the tracing parent to distinguish
134 between a syscall stop and SIGTRAP delivery */
135 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
136 ? 0x80 : 0));
137 /*
138 * this isn't the same as continuing with a signal, but it will do
139 * for normal use. strace only continues with a signal if the
140 * stopping signal is not SIGTRAP. -brl
141 */
142 if (current->exit_code) {
143 send_sig(current->exit_code, current, 1);
144 current->exit_code = 0;
145 }
146 regs->UCreg_ip = ip;
147
148 return current_thread_info()->syscall;
149}
diff --git a/arch/unicore32/kernel/puv3-core.c b/arch/unicore32/kernel/puv3-core.c
new file mode 100644
index 00000000000..8b1b6beb858
--- /dev/null
+++ b/arch/unicore32/kernel/puv3-core.c
@@ -0,0 +1,285 @@
1/*
2 * linux/arch/unicore32/kernel/puv3-core.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/init.h>
15#include <linux/device.h>
16#include <linux/sysdev.h>
17#include <linux/amba/bus.h>
18#include <linux/platform_device.h>
19#include <linux/io.h>
20#include <linux/cnt32_to_63.h>
21#include <linux/usb/musb.h>
22
23#include <asm/irq.h>
24#include <mach/hardware.h>
25#include <mach/pm.h>
26
27/*
28 * This is the PKUnity sched_clock implementation. This has
29 * a resolution of 271ns, and a maximum value of 32025597s (370 days).
30 *
31 * The return value is guaranteed to be monotonic in that range as
32 * long as there is always less than 582 seconds between successive
33 * calls to this function.
34 *
35 * ( * 1E9 / CLOCK_TICK_RATE ) -> about 2235/32
36 */
37unsigned long long sched_clock(void)
38{
39 unsigned long long v = cnt32_to_63(readl(OST_OSCR));
40
41 /* original conservative method, but overflow frequently
42 * v *= NSEC_PER_SEC >> 12;
43 * do_div(v, CLOCK_TICK_RATE >> 12);
44 */
45 v = ((v & 0x7fffffffffffffffULL) * 2235) >> 5;
46
47 return v;
48}
49
50static struct resource puv3_usb_resources[] = {
51 /* order is significant! */
52 {
53 .start = io_v2p(PKUNITY_USB_BASE),
54 .end = io_v2p(PKUNITY_USB_BASE) + 0x3ff,
55 .flags = IORESOURCE_MEM,
56 }, {
57 .start = IRQ_USB,
58 .flags = IORESOURCE_IRQ,
59 }, {
60 .start = IRQ_USB,
61 .flags = IORESOURCE_IRQ,
62 },
63};
64
65static struct musb_hdrc_config puv3_usb_config[] = {
66 {
67 .num_eps = 16,
68 .multipoint = 1,
69#ifdef CONFIG_USB_INVENTRA_DMA
70 .dma = 1,
71 .dma_channels = 8,
72#endif
73 },
74};
75
76static struct musb_hdrc_platform_data puv3_usb_plat = {
77 .mode = MUSB_HOST,
78 .min_power = 100,
79 .clock = 0,
80 .config = puv3_usb_config,
81};
82
83static struct resource puv3_mmc_resources[] = {
84 [0] = {
85 .start = io_v2p(PKUNITY_SDC_BASE),
86 .end = io_v2p(PKUNITY_SDC_BASE) + 0xfff,
87 .flags = IORESOURCE_MEM,
88 },
89 [1] = {
90 .start = IRQ_SDC,
91 .end = IRQ_SDC,
92 .flags = IORESOURCE_IRQ,
93 },
94};
95
96static struct resource puv3_unigfx_resources[] = {
97 [0] = {
98 .start = io_v2p(PKUNITY_UNIGFX_BASE),
99 .end = io_v2p(PKUNITY_UNIGFX_BASE) + 0xfff,
100 .flags = IORESOURCE_MEM,
101 },
102 [1] = {
103 .start = PKUNITY_UNIGFX_MMAP_BASE,
104 .end = PKUNITY_UNIGFX_MMAP_BASE + PKUNITY_UNIGFX_MMAP_SIZE,
105 .flags = IORESOURCE_MEM,
106 },
107};
108
109static struct resource puv3_rtc_resources[] = {
110 [0] = {
111 .start = io_v2p(PKUNITY_RTC_BASE),
112 .end = io_v2p(PKUNITY_RTC_BASE) + 0xff,
113 .flags = IORESOURCE_MEM,
114 },
115 [1] = {
116 .start = IRQ_RTCAlarm,
117 .end = IRQ_RTCAlarm,
118 .flags = IORESOURCE_IRQ,
119 },
120 [2] = {
121 .start = IRQ_RTC,
122 .end = IRQ_RTC,
123 .flags = IORESOURCE_IRQ
124 }
125};
126
127static struct resource puv3_pwm_resources[] = {
128 [0] = {
129 .start = io_v2p(PKUNITY_OST_BASE) + 0x80,
130 .end = io_v2p(PKUNITY_OST_BASE) + 0xff,
131 .flags = IORESOURCE_MEM,
132 },
133};
134
135static struct resource puv3_uart0_resources[] = {
136 [0] = {
137 .start = io_v2p(PKUNITY_UART0_BASE),
138 .end = io_v2p(PKUNITY_UART0_BASE) + 0xff,
139 .flags = IORESOURCE_MEM,
140 },
141 [1] = {
142 .start = IRQ_UART0,
143 .end = IRQ_UART0,
144 .flags = IORESOURCE_IRQ
145 }
146};
147
148static struct resource puv3_uart1_resources[] = {
149 [0] = {
150 .start = io_v2p(PKUNITY_UART1_BASE),
151 .end = io_v2p(PKUNITY_UART1_BASE) + 0xff,
152 .flags = IORESOURCE_MEM,
153 },
154 [1] = {
155 .start = IRQ_UART1,
156 .end = IRQ_UART1,
157 .flags = IORESOURCE_IRQ
158 }
159};
160
161static struct resource puv3_umal_resources[] = {
162 [0] = {
163 .start = io_v2p(PKUNITY_UMAL_BASE),
164 .end = io_v2p(PKUNITY_UMAL_BASE) + 0x1fff,
165 .flags = IORESOURCE_MEM,
166 },
167 [1] = {
168 .start = IRQ_UMAL,
169 .end = IRQ_UMAL,
170 .flags = IORESOURCE_IRQ
171 }
172};
173
174#ifdef CONFIG_PUV3_PM
175
176#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
177#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
178
179/*
180 * List of global PXA peripheral registers to preserve.
181 * More ones like CP and general purpose register values are preserved
182 * with the stack pointer in sleep.S.
183 */
184enum {
185 SLEEP_SAVE_PM_PLLDDRCFG,
186 SLEEP_SAVE_COUNT
187};
188
189
190static void puv3_cpu_pm_save(unsigned long *sleep_save)
191{
192/* SAVE(PM_PLLDDRCFG); */
193}
194
195static void puv3_cpu_pm_restore(unsigned long *sleep_save)
196{
197/* RESTORE(PM_PLLDDRCFG); */
198}
199
200static int puv3_cpu_pm_prepare(void)
201{
202 /* set resume return address */
203 writel(virt_to_phys(puv3_cpu_resume), PM_DIVCFG);
204 return 0;
205}
206
207static void puv3_cpu_pm_enter(suspend_state_t state)
208{
209 /* Clear reset status */
210 writel(RESETC_RSSR_HWR | RESETC_RSSR_WDR
211 | RESETC_RSSR_SMR | RESETC_RSSR_SWR, RESETC_RSSR);
212
213 switch (state) {
214/* case PM_SUSPEND_ON:
215 puv3_cpu_idle();
216 break; */
217 case PM_SUSPEND_MEM:
218 puv3_cpu_pm_prepare();
219 puv3_cpu_suspend(PM_PMCR_SFB);
220 break;
221 }
222}
223
224static int puv3_cpu_pm_valid(suspend_state_t state)
225{
226 return state == PM_SUSPEND_MEM;
227}
228
229static void puv3_cpu_pm_finish(void)
230{
231 /* ensure not to come back here if it wasn't intended */
232 /* PSPR = 0; */
233}
234
235static struct puv3_cpu_pm_fns puv3_cpu_pm_fnss = {
236 .save_count = SLEEP_SAVE_COUNT,
237 .valid = puv3_cpu_pm_valid,
238 .save = puv3_cpu_pm_save,
239 .restore = puv3_cpu_pm_restore,
240 .enter = puv3_cpu_pm_enter,
241 .prepare = puv3_cpu_pm_prepare,
242 .finish = puv3_cpu_pm_finish,
243};
244
245static void __init puv3_init_pm(void)
246{
247 puv3_cpu_pm_fns = &puv3_cpu_pm_fnss;
248}
249#else
250static inline void puv3_init_pm(void) {}
251#endif
252
253void puv3_ps2_init(void)
254{
255 struct clk *bclk32;
256
257 bclk32 = clk_get(NULL, "BUS32_CLK");
258 writel(clk_get_rate(bclk32) / 200000, PS2_CNT); /* should > 5us */
259}
260
261void __init puv3_core_init(void)
262{
263 puv3_init_pm();
264 puv3_ps2_init();
265
266 platform_device_register_simple("PKUnity-v3-RTC", -1,
267 puv3_rtc_resources, ARRAY_SIZE(puv3_rtc_resources));
268 platform_device_register_simple("PKUnity-v3-UMAL", -1,
269 puv3_umal_resources, ARRAY_SIZE(puv3_umal_resources));
270 platform_device_register_simple("PKUnity-v3-MMC", -1,
271 puv3_mmc_resources, ARRAY_SIZE(puv3_mmc_resources));
272 platform_device_register_simple("PKUnity-v3-UNIGFX", -1,
273 puv3_unigfx_resources, ARRAY_SIZE(puv3_unigfx_resources));
274 platform_device_register_simple("PKUnity-v3-PWM", -1,
275 puv3_pwm_resources, ARRAY_SIZE(puv3_pwm_resources));
276 platform_device_register_simple("PKUnity-v3-UART", 0,
277 puv3_uart0_resources, ARRAY_SIZE(puv3_uart0_resources));
278 platform_device_register_simple("PKUnity-v3-UART", 1,
279 puv3_uart1_resources, ARRAY_SIZE(puv3_uart1_resources));
280 platform_device_register_simple("PKUnity-v3-AC97", -1, NULL, 0);
281 platform_device_register_resndata(&platform_bus, "musb_hdrc", -1,
282 puv3_usb_resources, ARRAY_SIZE(puv3_usb_resources),
283 &puv3_usb_plat, sizeof(puv3_usb_plat));
284}
285
diff --git a/arch/unicore32/kernel/puv3-nb0916.c b/arch/unicore32/kernel/puv3-nb0916.c
new file mode 100644
index 00000000000..e731c561ed4
--- /dev/null
+++ b/arch/unicore32/kernel/puv3-nb0916.c
@@ -0,0 +1,145 @@
1/*
2 * linux/arch/unicore32/kernel/puv3-nb0916.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/init.h>
15#include <linux/device.h>
16#include <linux/sysdev.h>
17#include <linux/platform_device.h>
18#include <linux/mtd/physmap.h>
19#include <linux/io.h>
20#include <linux/reboot.h>
21#include <linux/interrupt.h>
22#include <linux/i2c.h>
23#include <linux/pwm_backlight.h>
24#include <linux/gpio.h>
25#include <linux/gpio_keys.h>
26#include <linux/input.h>
27
28#include <mach/hardware.h>
29
30static struct physmap_flash_data physmap_flash_data = {
31 .width = 1,
32};
33
34static struct resource physmap_flash_resource = {
35 .start = 0xFFF80000,
36 .end = 0xFFFFFFFF,
37 .flags = IORESOURCE_MEM,
38};
39
40static struct resource puv3_i2c_resources[] = {
41 [0] = {
42 .start = io_v2p(PKUNITY_I2C_BASE),
43 .end = io_v2p(PKUNITY_I2C_BASE) + 0xff,
44 .flags = IORESOURCE_MEM,
45 },
46 [1] = {
47 .start = IRQ_I2C,
48 .end = IRQ_I2C,
49 .flags = IORESOURCE_IRQ,
50 }
51};
52
53static struct platform_pwm_backlight_data nb0916_backlight_data = {
54 .pwm_id = 0,
55 .max_brightness = 100,
56 .dft_brightness = 100,
57 .pwm_period_ns = 70 * 1024,
58};
59
60static struct gpio_keys_button nb0916_gpio_keys[] = {
61 {
62 .type = EV_KEY,
63 .code = KEY_POWER,
64 .gpio = GPI_SOFF_REQ,
65 .desc = "Power Button",
66 .wakeup = 1,
67 .active_low = 1,
68 },
69 {
70 .type = EV_KEY,
71 .code = BTN_TOUCH,
72 .gpio = GPI_BTN_TOUCH,
73 .desc = "Touchpad Button",
74 .wakeup = 1,
75 .active_low = 1,
76 },
77};
78
79static struct gpio_keys_platform_data nb0916_gpio_button_data = {
80 .buttons = nb0916_gpio_keys,
81 .nbuttons = ARRAY_SIZE(nb0916_gpio_keys),
82};
83
84static irqreturn_t nb0916_lcdcaseoff_handler(int irq, void *dev_id)
85{
86 if (gpio_get_value(GPI_LCD_CASE_OFF))
87 gpio_set_value(GPO_LCD_EN, 1);
88 else
89 gpio_set_value(GPO_LCD_EN, 0);
90
91 return IRQ_HANDLED;
92}
93
94static irqreturn_t nb0916_overheat_handler(int irq, void *dev_id)
95{
96 machine_halt();
97 /* SYSTEM HALT, NO RETURN */
98 return IRQ_HANDLED;
99}
100
101static struct i2c_board_info __initdata puv3_i2c_devices[] = {
102 { I2C_BOARD_INFO("lm75", I2C_TAR_THERMAL), },
103 { I2C_BOARD_INFO("bq27200", I2C_TAR_PWIC), },
104 { I2C_BOARD_INFO("24c02", I2C_TAR_EEPROM), },
105};
106
107int __init mach_nb0916_init(void)
108{
109 i2c_register_board_info(0, puv3_i2c_devices,
110 ARRAY_SIZE(puv3_i2c_devices));
111
112 platform_device_register_simple("PKUnity-v3-I2C", -1,
113 puv3_i2c_resources, ARRAY_SIZE(puv3_i2c_resources));
114
115 platform_device_register_data(&platform_bus, "pwm-backlight", -1,
116 &nb0916_backlight_data, sizeof(nb0916_backlight_data));
117
118 platform_device_register_data(&platform_bus, "gpio-keys", -1,
119 &nb0916_gpio_button_data, sizeof(nb0916_gpio_button_data));
120
121 platform_device_register_resndata(&platform_bus, "physmap-flash", -1,
122 &physmap_flash_resource, 1,
123 &physmap_flash_data, sizeof(physmap_flash_data));
124
125 if (request_irq(gpio_to_irq(GPI_LCD_CASE_OFF),
126 &nb0916_lcdcaseoff_handler,
127 IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
128 "NB0916 lcd case off", NULL) < 0) {
129
130 printk(KERN_DEBUG "LCD-Case-OFF IRQ %d not available\n",
131 gpio_to_irq(GPI_LCD_CASE_OFF));
132 }
133
134 if (request_irq(gpio_to_irq(GPI_OTP_INT), &nb0916_overheat_handler,
135 IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
136 "NB0916 overheating protection", NULL) < 0) {
137
138 printk(KERN_DEBUG "Overheating Protection IRQ %d not available\n",
139 gpio_to_irq(GPI_OTP_INT));
140 }
141
142 return 0;
143}
144
145subsys_initcall_sync(mach_nb0916_init);
diff --git a/arch/unicore32/kernel/pwm.c b/arch/unicore32/kernel/pwm.c
new file mode 100644
index 00000000000..4615d51e3ba
--- /dev/null
+++ b/arch/unicore32/kernel/pwm.c
@@ -0,0 +1,263 @@
1/*
2 * linux/arch/unicore32/kernel/pwm.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18#include <linux/err.h>
19#include <linux/clk.h>
20#include <linux/io.h>
21#include <linux/pwm.h>
22
23#include <asm/div64.h>
24#include <mach/hardware.h>
25
26struct pwm_device {
27 struct list_head node;
28 struct platform_device *pdev;
29
30 const char *label;
31 struct clk *clk;
32 int clk_enabled;
33
34 unsigned int use_count;
35 unsigned int pwm_id;
36};
37
38/*
39 * period_ns = 10^9 * (PRESCALE + 1) * (PV + 1) / PWM_CLK_RATE
40 * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE
41 */
42int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
43{
44 unsigned long long c;
45 unsigned long period_cycles, prescale, pv, dc;
46
47 if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
48 return -EINVAL;
49
50 c = clk_get_rate(pwm->clk);
51 c = c * period_ns;
52 do_div(c, 1000000000);
53 period_cycles = c;
54
55 if (period_cycles < 1)
56 period_cycles = 1;
57 prescale = (period_cycles - 1) / 1024;
58 pv = period_cycles / (prescale + 1) - 1;
59
60 if (prescale > 63)
61 return -EINVAL;
62
63 if (duty_ns == period_ns)
64 dc = OST_PWMDCCR_FDCYCLE;
65 else
66 dc = (pv + 1) * duty_ns / period_ns;
67
68 /* NOTE: the clock to PWM has to be enabled first
69 * before writing to the registers
70 */
71 clk_enable(pwm->clk);
72 OST_PWMPWCR = prescale;
73 OST_PWMDCCR = pv - dc;
74 OST_PWMPCR = pv;
75 clk_disable(pwm->clk);
76
77 return 0;
78}
79EXPORT_SYMBOL(pwm_config);
80
81int pwm_enable(struct pwm_device *pwm)
82{
83 int rc = 0;
84
85 if (!pwm->clk_enabled) {
86 rc = clk_enable(pwm->clk);
87 if (!rc)
88 pwm->clk_enabled = 1;
89 }
90 return rc;
91}
92EXPORT_SYMBOL(pwm_enable);
93
94void pwm_disable(struct pwm_device *pwm)
95{
96 if (pwm->clk_enabled) {
97 clk_disable(pwm->clk);
98 pwm->clk_enabled = 0;
99 }
100}
101EXPORT_SYMBOL(pwm_disable);
102
103static DEFINE_MUTEX(pwm_lock);
104static LIST_HEAD(pwm_list);
105
106struct pwm_device *pwm_request(int pwm_id, const char *label)
107{
108 struct pwm_device *pwm;
109 int found = 0;
110
111 mutex_lock(&pwm_lock);
112
113 list_for_each_entry(pwm, &pwm_list, node) {
114 if (pwm->pwm_id == pwm_id) {
115 found = 1;
116 break;
117 }
118 }
119
120 if (found) {
121 if (pwm->use_count == 0) {
122 pwm->use_count++;
123 pwm->label = label;
124 } else
125 pwm = ERR_PTR(-EBUSY);
126 } else
127 pwm = ERR_PTR(-ENOENT);
128
129 mutex_unlock(&pwm_lock);
130 return pwm;
131}
132EXPORT_SYMBOL(pwm_request);
133
134void pwm_free(struct pwm_device *pwm)
135{
136 mutex_lock(&pwm_lock);
137
138 if (pwm->use_count) {
139 pwm->use_count--;
140 pwm->label = NULL;
141 } else
142 pr_warning("PWM device already freed\n");
143
144 mutex_unlock(&pwm_lock);
145}
146EXPORT_SYMBOL(pwm_free);
147
148static inline void __add_pwm(struct pwm_device *pwm)
149{
150 mutex_lock(&pwm_lock);
151 list_add_tail(&pwm->node, &pwm_list);
152 mutex_unlock(&pwm_lock);
153}
154
155static struct pwm_device *pwm_probe(struct platform_device *pdev,
156 unsigned int pwm_id, struct pwm_device *parent_pwm)
157{
158 struct pwm_device *pwm;
159 struct resource *r;
160 int ret = 0;
161
162 pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
163 if (pwm == NULL) {
164 dev_err(&pdev->dev, "failed to allocate memory\n");
165 return ERR_PTR(-ENOMEM);
166 }
167
168 pwm->clk = clk_get(NULL, "OST_CLK");
169 if (IS_ERR(pwm->clk)) {
170 ret = PTR_ERR(pwm->clk);
171 goto err_free;
172 }
173 pwm->clk_enabled = 0;
174
175 pwm->use_count = 0;
176 pwm->pwm_id = pwm_id;
177 pwm->pdev = pdev;
178
179 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
180 if (r == NULL) {
181 dev_err(&pdev->dev, "no memory resource defined\n");
182 ret = -ENODEV;
183 goto err_free_clk;
184 }
185
186 r = request_mem_region(r->start, resource_size(r), pdev->name);
187 if (r == NULL) {
188 dev_err(&pdev->dev, "failed to request memory resource\n");
189 ret = -EBUSY;
190 goto err_free_clk;
191 }
192
193 __add_pwm(pwm);
194 platform_set_drvdata(pdev, pwm);
195 return pwm;
196
197err_free_clk:
198 clk_put(pwm->clk);
199err_free:
200 kfree(pwm);
201 return ERR_PTR(ret);
202}
203
204static int __devinit puv3_pwm_probe(struct platform_device *pdev)
205{
206 struct pwm_device *pwm = pwm_probe(pdev, pdev->id, NULL);
207
208 if (IS_ERR(pwm))
209 return PTR_ERR(pwm);
210
211 return 0;
212}
213
214static int __devexit pwm_remove(struct platform_device *pdev)
215{
216 struct pwm_device *pwm;
217 struct resource *r;
218
219 pwm = platform_get_drvdata(pdev);
220 if (pwm == NULL)
221 return -ENODEV;
222
223 mutex_lock(&pwm_lock);
224 list_del(&pwm->node);
225 mutex_unlock(&pwm_lock);
226
227 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
228 release_mem_region(r->start, resource_size(r));
229
230 clk_put(pwm->clk);
231 kfree(pwm);
232 return 0;
233}
234
235static struct platform_driver puv3_pwm_driver = {
236 .driver = {
237 .name = "PKUnity-v3-PWM",
238 },
239 .probe = puv3_pwm_probe,
240 .remove = __devexit_p(pwm_remove),
241};
242
243static int __init pwm_init(void)
244{
245 int ret = 0;
246
247 ret = platform_driver_register(&puv3_pwm_driver);
248 if (ret) {
249 printk(KERN_ERR "failed to register puv3_pwm_driver\n");
250 return ret;
251 }
252
253 return ret;
254}
255arch_initcall(pwm_init);
256
257static void __exit pwm_exit(void)
258{
259 platform_driver_unregister(&puv3_pwm_driver);
260}
261module_exit(pwm_exit);
262
263MODULE_LICENSE("GPL v2");
diff --git a/arch/unicore32/kernel/rtc.c b/arch/unicore32/kernel/rtc.c
new file mode 100644
index 00000000000..c5f068295b5
--- /dev/null
+++ b/arch/unicore32/kernel/rtc.c
@@ -0,0 +1,380 @@
1/*
2 * linux/arch/unicore32/kernel/rtc.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/module.h>
15#include <linux/fs.h>
16#include <linux/string.h>
17#include <linux/init.h>
18#include <linux/platform_device.h>
19#include <linux/interrupt.h>
20#include <linux/rtc.h>
21#include <linux/bcd.h>
22#include <linux/clk.h>
23#include <linux/log2.h>
24#include <linux/slab.h>
25#include <linux/uaccess.h>
26#include <linux/io.h>
27
28#include <asm/irq.h>
29#include <mach/hardware.h>
30
31static struct resource *puv3_rtc_mem;
32
33static int puv3_rtc_alarmno = IRQ_RTCAlarm;
34static int puv3_rtc_tickno = IRQ_RTC;
35
36static DEFINE_SPINLOCK(puv3_rtc_pie_lock);
37
38/* IRQ Handlers */
39
40static irqreturn_t puv3_rtc_alarmirq(int irq, void *id)
41{
42 struct rtc_device *rdev = id;
43
44 writel(readl(RTC_RTSR) | RTC_RTSR_AL, RTC_RTSR);
45 rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
46 return IRQ_HANDLED;
47}
48
49static irqreturn_t puv3_rtc_tickirq(int irq, void *id)
50{
51 struct rtc_device *rdev = id;
52
53 writel(readl(RTC_RTSR) | RTC_RTSR_HZ, RTC_RTSR);
54 rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
55 return IRQ_HANDLED;
56}
57
58/* Update control registers */
59static void puv3_rtc_setaie(int to)
60{
61 unsigned int tmp;
62
63 pr_debug("%s: aie=%d\n", __func__, to);
64
65 tmp = readl(RTC_RTSR) & ~RTC_RTSR_ALE;
66
67 if (to)
68 tmp |= RTC_RTSR_ALE;
69
70 writel(tmp, RTC_RTSR);
71}
72
73static int puv3_rtc_setpie(struct device *dev, int enabled)
74{
75 unsigned int tmp;
76
77 pr_debug("%s: pie=%d\n", __func__, enabled);
78
79 spin_lock_irq(&puv3_rtc_pie_lock);
80 tmp = readl(RTC_RTSR) & ~RTC_RTSR_HZE;
81
82 if (enabled)
83 tmp |= RTC_RTSR_HZE;
84
85 writel(tmp, RTC_RTSR);
86 spin_unlock_irq(&puv3_rtc_pie_lock);
87
88 return 0;
89}
90
91static int puv3_rtc_setfreq(struct device *dev, int freq)
92{
93 return 0;
94}
95
96/* Time read/write */
97
98static int puv3_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
99{
100 rtc_time_to_tm(readl(RTC_RCNR), rtc_tm);
101
102 pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
103 rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
104 rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
105
106 return 0;
107}
108
109static int puv3_rtc_settime(struct device *dev, struct rtc_time *tm)
110{
111 unsigned long rtc_count = 0;
112
113 pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",
114 tm->tm_year, tm->tm_mon, tm->tm_mday,
115 tm->tm_hour, tm->tm_min, tm->tm_sec);
116
117 rtc_tm_to_time(tm, &rtc_count);
118 writel(rtc_count, RTC_RCNR);
119
120 return 0;
121}
122
123static int puv3_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
124{
125 struct rtc_time *alm_tm = &alrm->time;
126
127 rtc_time_to_tm(readl(RTC_RTAR), alm_tm);
128
129 alrm->enabled = readl(RTC_RTSR) & RTC_RTSR_ALE;
130
131 pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
132 alrm->enabled,
133 alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
134 alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
135
136 return 0;
137}
138
139static int puv3_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
140{
141 struct rtc_time *tm = &alrm->time;
142 unsigned long rtcalarm_count = 0;
143
144 pr_debug("puv3_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
145 alrm->enabled,
146 tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
147 tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
148
149 rtc_tm_to_time(tm, &rtcalarm_count);
150 writel(rtcalarm_count, RTC_RTAR);
151
152 puv3_rtc_setaie(alrm->enabled);
153
154 if (alrm->enabled)
155 enable_irq_wake(puv3_rtc_alarmno);
156 else
157 disable_irq_wake(puv3_rtc_alarmno);
158
159 return 0;
160}
161
162static int puv3_rtc_proc(struct device *dev, struct seq_file *seq)
163{
164 seq_printf(seq, "periodic_IRQ\t: %s\n",
165 (readl(RTC_RTSR) & RTC_RTSR_HZE) ? "yes" : "no");
166 return 0;
167}
168
169static int puv3_rtc_open(struct device *dev)
170{
171 struct platform_device *pdev = to_platform_device(dev);
172 struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
173 int ret;
174
175 ret = request_irq(puv3_rtc_alarmno, puv3_rtc_alarmirq,
176 IRQF_DISABLED, "pkunity-rtc alarm", rtc_dev);
177
178 if (ret) {
179 dev_err(dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret);
180 return ret;
181 }
182
183 ret = request_irq(puv3_rtc_tickno, puv3_rtc_tickirq,
184 IRQF_DISABLED, "pkunity-rtc tick", rtc_dev);
185
186 if (ret) {
187 dev_err(dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret);
188 goto tick_err;
189 }
190
191 return ret;
192
193 tick_err:
194 free_irq(puv3_rtc_alarmno, rtc_dev);
195 return ret;
196}
197
198static void puv3_rtc_release(struct device *dev)
199{
200 struct platform_device *pdev = to_platform_device(dev);
201 struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
202
203 /* do not clear AIE here, it may be needed for wake */
204
205 puv3_rtc_setpie(dev, 0);
206 free_irq(puv3_rtc_alarmno, rtc_dev);
207 free_irq(puv3_rtc_tickno, rtc_dev);
208}
209
210static const struct rtc_class_ops puv3_rtcops = {
211 .open = puv3_rtc_open,
212 .release = puv3_rtc_release,
213 .read_time = puv3_rtc_gettime,
214 .set_time = puv3_rtc_settime,
215 .read_alarm = puv3_rtc_getalarm,
216 .set_alarm = puv3_rtc_setalarm,
217 .irq_set_freq = puv3_rtc_setfreq,
218 .irq_set_state = puv3_rtc_setpie,
219 .proc = puv3_rtc_proc,
220};
221
222static void puv3_rtc_enable(struct platform_device *pdev, int en)
223{
224 if (!en) {
225 writel(readl(RTC_RTSR) & ~RTC_RTSR_HZE, RTC_RTSR);
226 } else {
227 /* re-enable the device, and check it is ok */
228
229 if ((readl(RTC_RTSR) & RTC_RTSR_HZE) == 0) {
230 dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
231 writel(readl(RTC_RTSR) | RTC_RTSR_HZE, RTC_RTSR);
232 }
233 }
234}
235
236static int puv3_rtc_remove(struct platform_device *dev)
237{
238 struct rtc_device *rtc = platform_get_drvdata(dev);
239
240 platform_set_drvdata(dev, NULL);
241 rtc_device_unregister(rtc);
242
243 puv3_rtc_setpie(&dev->dev, 0);
244 puv3_rtc_setaie(0);
245
246 release_resource(puv3_rtc_mem);
247 kfree(puv3_rtc_mem);
248
249 return 0;
250}
251
252static int puv3_rtc_probe(struct platform_device *pdev)
253{
254 struct rtc_device *rtc;
255 struct resource *res;
256 int ret;
257
258 pr_debug("%s: probe=%p\n", __func__, pdev);
259
260 /* find the IRQs */
261
262 puv3_rtc_tickno = platform_get_irq(pdev, 1);
263 if (puv3_rtc_tickno < 0) {
264 dev_err(&pdev->dev, "no irq for rtc tick\n");
265 return -ENOENT;
266 }
267
268 puv3_rtc_alarmno = platform_get_irq(pdev, 0);
269 if (puv3_rtc_alarmno < 0) {
270 dev_err(&pdev->dev, "no irq for alarm\n");
271 return -ENOENT;
272 }
273
274 pr_debug("PKUnity_rtc: tick irq %d, alarm irq %d\n",
275 puv3_rtc_tickno, puv3_rtc_alarmno);
276
277 /* get the memory region */
278
279 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
280 if (res == NULL) {
281 dev_err(&pdev->dev, "failed to get memory region resource\n");
282 return -ENOENT;
283 }
284
285 puv3_rtc_mem = request_mem_region(res->start,
286 res->end-res->start+1,
287 pdev->name);
288
289 if (puv3_rtc_mem == NULL) {
290 dev_err(&pdev->dev, "failed to reserve memory region\n");
291 ret = -ENOENT;
292 goto err_nores;
293 }
294
295 puv3_rtc_enable(pdev, 1);
296
297 puv3_rtc_setfreq(&pdev->dev, 1);
298
299 /* register RTC and exit */
300
301 rtc = rtc_device_register("pkunity", &pdev->dev, &puv3_rtcops,
302 THIS_MODULE);
303
304 if (IS_ERR(rtc)) {
305 dev_err(&pdev->dev, "cannot attach rtc\n");
306 ret = PTR_ERR(rtc);
307 goto err_nortc;
308 }
309
310 /* platform setup code should have handled this; sigh */
311 if (!device_can_wakeup(&pdev->dev))
312 device_init_wakeup(&pdev->dev, 1);
313
314 platform_set_drvdata(pdev, rtc);
315 return 0;
316
317 err_nortc:
318 puv3_rtc_enable(pdev, 0);
319 release_resource(puv3_rtc_mem);
320
321 err_nores:
322 return ret;
323}
324
325#ifdef CONFIG_PM
326
327/* RTC Power management control */
328
329static int ticnt_save;
330
331static int puv3_rtc_suspend(struct platform_device *pdev, pm_message_t state)
332{
333 /* save RTAR for anyone using periodic interrupts */
334 ticnt_save = readl(RTC_RTAR);
335 puv3_rtc_enable(pdev, 0);
336 return 0;
337}
338
339static int puv3_rtc_resume(struct platform_device *pdev)
340{
341 puv3_rtc_enable(pdev, 1);
342 writel(ticnt_save, RTC_RTAR);
343 return 0;
344}
345#else
346#define puv3_rtc_suspend NULL
347#define puv3_rtc_resume NULL
348#endif
349
350static struct platform_driver puv3_rtcdrv = {
351 .probe = puv3_rtc_probe,
352 .remove = __devexit_p(puv3_rtc_remove),
353 .suspend = puv3_rtc_suspend,
354 .resume = puv3_rtc_resume,
355 .driver = {
356 .name = "PKUnity-v3-RTC",
357 .owner = THIS_MODULE,
358 }
359};
360
361static char __initdata banner[] = "PKUnity-v3 RTC, (c) 2009 PKUnity Co.\n";
362
363static int __init puv3_rtc_init(void)
364{
365 printk(banner);
366 return platform_driver_register(&puv3_rtcdrv);
367}
368
369static void __exit puv3_rtc_exit(void)
370{
371 platform_driver_unregister(&puv3_rtcdrv);
372}
373
374module_init(puv3_rtc_init);
375module_exit(puv3_rtc_exit);
376
377MODULE_DESCRIPTION("RTC Driver for the PKUnity v3 chip");
378MODULE_AUTHOR("Hu Dongliang");
379MODULE_LICENSE("GPL v2");
380
diff --git a/arch/unicore32/kernel/setup.c b/arch/unicore32/kernel/setup.c
new file mode 100644
index 00000000000..1e175a82844
--- /dev/null
+++ b/arch/unicore32/kernel/setup.c
@@ -0,0 +1,360 @@
1/*
2 * linux/arch/unicore32/kernel/setup.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/stddef.h>
15#include <linux/ioport.h>
16#include <linux/delay.h>
17#include <linux/utsname.h>
18#include <linux/initrd.h>
19#include <linux/console.h>
20#include <linux/bootmem.h>
21#include <linux/seq_file.h>
22#include <linux/screen_info.h>
23#include <linux/init.h>
24#include <linux/root_dev.h>
25#include <linux/cpu.h>
26#include <linux/interrupt.h>
27#include <linux/smp.h>
28#include <linux/fs.h>
29#include <linux/proc_fs.h>
30#include <linux/memblock.h>
31#include <linux/elf.h>
32#include <linux/io.h>
33
34#include <asm/cputype.h>
35#include <asm/sections.h>
36#include <asm/setup.h>
37#include <asm/cacheflush.h>
38#include <asm/tlbflush.h>
39#include <asm/traps.h>
40
41#include "setup.h"
42
43#ifndef MEM_SIZE
44#define MEM_SIZE (16*1024*1024)
45#endif
46
47struct stack {
48 u32 irq[3];
49 u32 abt[3];
50 u32 und[3];
51} ____cacheline_aligned;
52
53static struct stack stacks[NR_CPUS];
54
55char elf_platform[ELF_PLATFORM_SIZE];
56EXPORT_SYMBOL(elf_platform);
57
58static char __initdata cmd_line[COMMAND_LINE_SIZE];
59
60static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
61
62/*
63 * Standard memory resources
64 */
65static struct resource mem_res[] = {
66 {
67 .name = "Video RAM",
68 .start = 0,
69 .end = 0,
70 .flags = IORESOURCE_MEM
71 },
72 {
73 .name = "Kernel text",
74 .start = 0,
75 .end = 0,
76 .flags = IORESOURCE_MEM
77 },
78 {
79 .name = "Kernel data",
80 .start = 0,
81 .end = 0,
82 .flags = IORESOURCE_MEM
83 }
84};
85
86#define video_ram mem_res[0]
87#define kernel_code mem_res[1]
88#define kernel_data mem_res[2]
89
90/*
91 * These functions re-use the assembly code in head.S, which
92 * already provide the required functionality.
93 */
94static void __init setup_processor(void)
95{
96 printk(KERN_DEFAULT "CPU: UniCore-II [%08x] revision %d, cr=%08lx\n",
97 uc32_cpuid, (int)(uc32_cpuid >> 16) & 15, cr_alignment);
98
99 sprintf(init_utsname()->machine, "puv3");
100 sprintf(elf_platform, "ucv2");
101}
102
103/*
104 * cpu_init - initialise one CPU.
105 *
106 * cpu_init sets up the per-CPU stacks.
107 */
108void cpu_init(void)
109{
110 unsigned int cpu = smp_processor_id();
111 struct stack *stk = &stacks[cpu];
112
113 /*
114 * setup stacks for re-entrant exception handlers
115 */
116 __asm__ (
117 "mov.a asr, %1\n\t"
118 "add sp, %0, %2\n\t"
119 "mov.a asr, %3\n\t"
120 "add sp, %0, %4\n\t"
121 "mov.a asr, %5\n\t"
122 "add sp, %0, %6\n\t"
123 "mov.a asr, %7"
124 :
125 : "r" (stk),
126 "r" (PSR_R_BIT | PSR_I_BIT | INTR_MODE),
127 "I" (offsetof(struct stack, irq[0])),
128 "r" (PSR_R_BIT | PSR_I_BIT | ABRT_MODE),
129 "I" (offsetof(struct stack, abt[0])),
130 "r" (PSR_R_BIT | PSR_I_BIT | EXTN_MODE),
131 "I" (offsetof(struct stack, und[0])),
132 "r" (PSR_R_BIT | PSR_I_BIT | PRIV_MODE)
133 : "r30", "cc");
134}
135
136static int __init uc32_add_memory(unsigned long start, unsigned long size)
137{
138 struct membank *bank = &meminfo.bank[meminfo.nr_banks];
139
140 if (meminfo.nr_banks >= NR_BANKS) {
141 printk(KERN_CRIT "NR_BANKS too low, "
142 "ignoring memory at %#lx\n", start);
143 return -EINVAL;
144 }
145
146 /*
147 * Ensure that start/size are aligned to a page boundary.
148 * Size is appropriately rounded down, start is rounded up.
149 */
150 size -= start & ~PAGE_MASK;
151
152 bank->start = PAGE_ALIGN(start);
153 bank->size = size & PAGE_MASK;
154
155 /*
156 * Check whether this memory region has non-zero size or
157 * invalid node number.
158 */
159 if (bank->size == 0)
160 return -EINVAL;
161
162 meminfo.nr_banks++;
163 return 0;
164}
165
166/*
167 * Pick out the memory size. We look for mem=size@start,
168 * where start and size are "size[KkMm]"
169 */
170static int __init early_mem(char *p)
171{
172 static int usermem __initdata = 1;
173 unsigned long size, start;
174 char *endp;
175
176 /*
177 * If the user specifies memory size, we
178 * blow away any automatically generated
179 * size.
180 */
181 if (usermem) {
182 usermem = 0;
183 meminfo.nr_banks = 0;
184 }
185
186 start = PHYS_OFFSET;
187 size = memparse(p, &endp);
188 if (*endp == '@')
189 start = memparse(endp + 1, NULL);
190
191 uc32_add_memory(start, size);
192
193 return 0;
194}
195early_param("mem", early_mem);
196
197static void __init
198request_standard_resources(struct meminfo *mi)
199{
200 struct resource *res;
201 int i;
202
203 kernel_code.start = virt_to_phys(_stext);
204 kernel_code.end = virt_to_phys(_etext - 1);
205 kernel_data.start = virt_to_phys(_sdata);
206 kernel_data.end = virt_to_phys(_end - 1);
207
208 for (i = 0; i < mi->nr_banks; i++) {
209 if (mi->bank[i].size == 0)
210 continue;
211
212 res = alloc_bootmem_low(sizeof(*res));
213 res->name = "System RAM";
214 res->start = mi->bank[i].start;
215 res->end = mi->bank[i].start + mi->bank[i].size - 1;
216 res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
217
218 request_resource(&iomem_resource, res);
219
220 if (kernel_code.start >= res->start &&
221 kernel_code.end <= res->end)
222 request_resource(res, &kernel_code);
223 if (kernel_data.start >= res->start &&
224 kernel_data.end <= res->end)
225 request_resource(res, &kernel_data);
226 }
227
228 video_ram.start = PKUNITY_UNIGFX_MMAP_BASE;
229 video_ram.end = PKUNITY_UNIGFX_MMAP_BASE + PKUNITY_UNIGFX_MMAP_SIZE;
230 request_resource(&iomem_resource, &video_ram);
231}
232
233static void (*init_machine)(void) __initdata;
234
235static int __init customize_machine(void)
236{
237 /* customizes platform devices, or adds new ones */
238 if (init_machine)
239 init_machine();
240 return 0;
241}
242arch_initcall(customize_machine);
243
244void __init setup_arch(char **cmdline_p)
245{
246 char *from = default_command_line;
247
248 setup_processor();
249
250 init_mm.start_code = (unsigned long) _stext;
251 init_mm.end_code = (unsigned long) _etext;
252 init_mm.end_data = (unsigned long) _edata;
253 init_mm.brk = (unsigned long) _end;
254
255 /* parse_early_param needs a boot_command_line */
256 strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
257
258 /* populate cmd_line too for later use, preserving boot_command_line */
259 strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
260 *cmdline_p = cmd_line;
261
262 parse_early_param();
263
264 uc32_memblock_init(&meminfo);
265
266 paging_init();
267 request_standard_resources(&meminfo);
268
269 cpu_init();
270
271 /*
272 * Set up various architecture-specific pointers
273 */
274 init_machine = puv3_core_init;
275
276#ifdef CONFIG_VT
277#if defined(CONFIG_VGA_CONSOLE)
278 conswitchp = &vga_con;
279#elif defined(CONFIG_DUMMY_CONSOLE)
280 conswitchp = &dummy_con;
281#endif
282#endif
283 early_trap_init();
284}
285
286static struct cpu cpuinfo_unicore;
287
288static int __init topology_init(void)
289{
290 int i;
291
292 for_each_possible_cpu(i)
293 register_cpu(&cpuinfo_unicore, i);
294
295 return 0;
296}
297subsys_initcall(topology_init);
298
299#ifdef CONFIG_HAVE_PROC_CPU
300static int __init proc_cpu_init(void)
301{
302 struct proc_dir_entry *res;
303
304 res = proc_mkdir("cpu", NULL);
305 if (!res)
306 return -ENOMEM;
307 return 0;
308}
309fs_initcall(proc_cpu_init);
310#endif
311
312static int c_show(struct seq_file *m, void *v)
313{
314 seq_printf(m, "Processor\t: UniCore-II rev %d (%s)\n",
315 (int)(uc32_cpuid >> 16) & 15, elf_platform);
316
317 seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
318 loops_per_jiffy / (500000/HZ),
319 (loops_per_jiffy / (5000/HZ)) % 100);
320
321 /* dump out the processor features */
322 seq_puts(m, "Features\t: CMOV UC-F64");
323
324 seq_printf(m, "\nCPU implementer\t: 0x%02x\n", uc32_cpuid >> 24);
325 seq_printf(m, "CPU architecture: 2\n");
326 seq_printf(m, "CPU revision\t: %d\n", (uc32_cpuid >> 16) & 15);
327
328 seq_printf(m, "Cache type\t: write-back\n"
329 "Cache clean\t: cp0 c5 ops\n"
330 "Cache lockdown\t: not support\n"
331 "Cache format\t: Harvard\n");
332
333 seq_puts(m, "\n");
334
335 seq_printf(m, "Hardware\t: PKUnity v3\n");
336
337 return 0;
338}
339
340static void *c_start(struct seq_file *m, loff_t *pos)
341{
342 return *pos < 1 ? (void *)1 : NULL;
343}
344
345static void *c_next(struct seq_file *m, void *v, loff_t *pos)
346{
347 ++*pos;
348 return NULL;
349}
350
351static void c_stop(struct seq_file *m, void *v)
352{
353}
354
355const struct seq_operations cpuinfo_op = {
356 .start = c_start,
357 .next = c_next,
358 .stop = c_stop,
359 .show = c_show
360};
diff --git a/arch/unicore32/kernel/setup.h b/arch/unicore32/kernel/setup.h
new file mode 100644
index 00000000000..dcd1306eb5c
--- /dev/null
+++ b/arch/unicore32/kernel/setup.h
@@ -0,0 +1,30 @@
1/*
2 * linux/arch/unicore32/kernel/setup.h
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#ifndef __UNICORE_KERNEL_SETUP_H__
13#define __UNICORE_KERNEL_SETUP_H__
14
15extern void paging_init(void);
16extern void puv3_core_init(void);
17
18extern void puv3_ps2_init(void);
19extern void pci_puv3_preinit(void);
20extern void __init puv3_init_gpio(void);
21
22extern void setup_mm_for_reboot(char mode);
23
24extern char __stubs_start[], __stubs_end[];
25extern char __vectors_start[], __vectors_end[];
26
27extern void kernel_thread_helper(void);
28
29extern void __init early_signal_init(void);
30#endif
diff --git a/arch/unicore32/kernel/signal.c b/arch/unicore32/kernel/signal.c
new file mode 100644
index 00000000000..b163fca5678
--- /dev/null
+++ b/arch/unicore32/kernel/signal.c
@@ -0,0 +1,494 @@
1/*
2 * linux/arch/unicore32/kernel/signal.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/errno.h>
13#include <linux/signal.h>
14#include <linux/personality.h>
15#include <linux/freezer.h>
16#include <linux/uaccess.h>
17#include <linux/tracehook.h>
18#include <linux/elf.h>
19#include <linux/unistd.h>
20
21#include <asm/cacheflush.h>
22#include <asm/ucontext.h>
23
24#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
25
26/*
27 * For UniCore syscalls, we encode the syscall number into the instruction.
28 */
29#define SWI_SYS_SIGRETURN (0xff000000) /* error number for new abi */
30#define SWI_SYS_RT_SIGRETURN (0xff000000 | (__NR_rt_sigreturn))
31#define SWI_SYS_RESTART (0xff000000 | (__NR_restart_syscall))
32
33#define KERN_SIGRETURN_CODE (KUSER_VECPAGE_BASE + 0x00000500)
34#define KERN_RESTART_CODE (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes))
35
36const unsigned long sigreturn_codes[3] = {
37 SWI_SYS_SIGRETURN, SWI_SYS_RT_SIGRETURN,
38};
39
40const unsigned long syscall_restart_code[2] = {
41 SWI_SYS_RESTART, /* swi __NR_restart_syscall */
42 0x69efc004, /* ldr pc, [sp], #4 */
43};
44
45/*
46 * Do a signal return; undo the signal stack. These are aligned to 64-bit.
47 */
48struct sigframe {
49 struct ucontext uc;
50 unsigned long retcode[2];
51};
52
53struct rt_sigframe {
54 struct siginfo info;
55 struct sigframe sig;
56};
57
58static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
59{
60 sigset_t set;
61 int err;
62
63 err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
64 if (err == 0) {
65 sigdelsetmask(&set, ~_BLOCKABLE);
66 spin_lock_irq(&current->sighand->siglock);
67 current->blocked = set;
68 recalc_sigpending();
69 spin_unlock_irq(&current->sighand->siglock);
70 }
71
72 err |= __get_user(regs->UCreg_00, &sf->uc.uc_mcontext.regs.UCreg_00);
73 err |= __get_user(regs->UCreg_01, &sf->uc.uc_mcontext.regs.UCreg_01);
74 err |= __get_user(regs->UCreg_02, &sf->uc.uc_mcontext.regs.UCreg_02);
75 err |= __get_user(regs->UCreg_03, &sf->uc.uc_mcontext.regs.UCreg_03);
76 err |= __get_user(regs->UCreg_04, &sf->uc.uc_mcontext.regs.UCreg_04);
77 err |= __get_user(regs->UCreg_05, &sf->uc.uc_mcontext.regs.UCreg_05);
78 err |= __get_user(regs->UCreg_06, &sf->uc.uc_mcontext.regs.UCreg_06);
79 err |= __get_user(regs->UCreg_07, &sf->uc.uc_mcontext.regs.UCreg_07);
80 err |= __get_user(regs->UCreg_08, &sf->uc.uc_mcontext.regs.UCreg_08);
81 err |= __get_user(regs->UCreg_09, &sf->uc.uc_mcontext.regs.UCreg_09);
82 err |= __get_user(regs->UCreg_10, &sf->uc.uc_mcontext.regs.UCreg_10);
83 err |= __get_user(regs->UCreg_11, &sf->uc.uc_mcontext.regs.UCreg_11);
84 err |= __get_user(regs->UCreg_12, &sf->uc.uc_mcontext.regs.UCreg_12);
85 err |= __get_user(regs->UCreg_13, &sf->uc.uc_mcontext.regs.UCreg_13);
86 err |= __get_user(regs->UCreg_14, &sf->uc.uc_mcontext.regs.UCreg_14);
87 err |= __get_user(regs->UCreg_15, &sf->uc.uc_mcontext.regs.UCreg_15);
88 err |= __get_user(regs->UCreg_16, &sf->uc.uc_mcontext.regs.UCreg_16);
89 err |= __get_user(regs->UCreg_17, &sf->uc.uc_mcontext.regs.UCreg_17);
90 err |= __get_user(regs->UCreg_18, &sf->uc.uc_mcontext.regs.UCreg_18);
91 err |= __get_user(regs->UCreg_19, &sf->uc.uc_mcontext.regs.UCreg_19);
92 err |= __get_user(regs->UCreg_20, &sf->uc.uc_mcontext.regs.UCreg_20);
93 err |= __get_user(regs->UCreg_21, &sf->uc.uc_mcontext.regs.UCreg_21);
94 err |= __get_user(regs->UCreg_22, &sf->uc.uc_mcontext.regs.UCreg_22);
95 err |= __get_user(regs->UCreg_23, &sf->uc.uc_mcontext.regs.UCreg_23);
96 err |= __get_user(regs->UCreg_24, &sf->uc.uc_mcontext.regs.UCreg_24);
97 err |= __get_user(regs->UCreg_25, &sf->uc.uc_mcontext.regs.UCreg_25);
98 err |= __get_user(regs->UCreg_26, &sf->uc.uc_mcontext.regs.UCreg_26);
99 err |= __get_user(regs->UCreg_fp, &sf->uc.uc_mcontext.regs.UCreg_fp);
100 err |= __get_user(regs->UCreg_ip, &sf->uc.uc_mcontext.regs.UCreg_ip);
101 err |= __get_user(regs->UCreg_sp, &sf->uc.uc_mcontext.regs.UCreg_sp);
102 err |= __get_user(regs->UCreg_lr, &sf->uc.uc_mcontext.regs.UCreg_lr);
103 err |= __get_user(regs->UCreg_pc, &sf->uc.uc_mcontext.regs.UCreg_pc);
104 err |= __get_user(regs->UCreg_asr, &sf->uc.uc_mcontext.regs.UCreg_asr);
105
106 err |= !valid_user_regs(regs);
107
108 return err;
109}
110
111asmlinkage int __sys_rt_sigreturn(struct pt_regs *regs)
112{
113 struct rt_sigframe __user *frame;
114
115 /* Always make any pending restarted system calls return -EINTR */
116 current_thread_info()->restart_block.fn = do_no_restart_syscall;
117
118 /*
119 * Since we stacked the signal on a 64-bit boundary,
120 * then 'sp' should be word aligned here. If it's
121 * not, then the user is trying to mess with us.
122 */
123 if (regs->UCreg_sp & 7)
124 goto badframe;
125
126 frame = (struct rt_sigframe __user *)regs->UCreg_sp;
127
128 if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
129 goto badframe;
130
131 if (restore_sigframe(regs, &frame->sig))
132 goto badframe;
133
134 if (do_sigaltstack(&frame->sig.uc.uc_stack, NULL, regs->UCreg_sp)
135 == -EFAULT)
136 goto badframe;
137
138 return regs->UCreg_00;
139
140badframe:
141 force_sig(SIGSEGV, current);
142 return 0;
143}
144
145static int setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs,
146 sigset_t *set)
147{
148 int err = 0;
149
150 err |= __put_user(regs->UCreg_00, &sf->uc.uc_mcontext.regs.UCreg_00);
151 err |= __put_user(regs->UCreg_01, &sf->uc.uc_mcontext.regs.UCreg_01);
152 err |= __put_user(regs->UCreg_02, &sf->uc.uc_mcontext.regs.UCreg_02);
153 err |= __put_user(regs->UCreg_03, &sf->uc.uc_mcontext.regs.UCreg_03);
154 err |= __put_user(regs->UCreg_04, &sf->uc.uc_mcontext.regs.UCreg_04);
155 err |= __put_user(regs->UCreg_05, &sf->uc.uc_mcontext.regs.UCreg_05);
156 err |= __put_user(regs->UCreg_06, &sf->uc.uc_mcontext.regs.UCreg_06);
157 err |= __put_user(regs->UCreg_07, &sf->uc.uc_mcontext.regs.UCreg_07);
158 err |= __put_user(regs->UCreg_08, &sf->uc.uc_mcontext.regs.UCreg_08);
159 err |= __put_user(regs->UCreg_09, &sf->uc.uc_mcontext.regs.UCreg_09);
160 err |= __put_user(regs->UCreg_10, &sf->uc.uc_mcontext.regs.UCreg_10);
161 err |= __put_user(regs->UCreg_11, &sf->uc.uc_mcontext.regs.UCreg_11);
162 err |= __put_user(regs->UCreg_12, &sf->uc.uc_mcontext.regs.UCreg_12);
163 err |= __put_user(regs->UCreg_13, &sf->uc.uc_mcontext.regs.UCreg_13);
164 err |= __put_user(regs->UCreg_14, &sf->uc.uc_mcontext.regs.UCreg_14);
165 err |= __put_user(regs->UCreg_15, &sf->uc.uc_mcontext.regs.UCreg_15);
166 err |= __put_user(regs->UCreg_16, &sf->uc.uc_mcontext.regs.UCreg_16);
167 err |= __put_user(regs->UCreg_17, &sf->uc.uc_mcontext.regs.UCreg_17);
168 err |= __put_user(regs->UCreg_18, &sf->uc.uc_mcontext.regs.UCreg_18);
169 err |= __put_user(regs->UCreg_19, &sf->uc.uc_mcontext.regs.UCreg_19);
170 err |= __put_user(regs->UCreg_20, &sf->uc.uc_mcontext.regs.UCreg_20);
171 err |= __put_user(regs->UCreg_21, &sf->uc.uc_mcontext.regs.UCreg_21);
172 err |= __put_user(regs->UCreg_22, &sf->uc.uc_mcontext.regs.UCreg_22);
173 err |= __put_user(regs->UCreg_23, &sf->uc.uc_mcontext.regs.UCreg_23);
174 err |= __put_user(regs->UCreg_24, &sf->uc.uc_mcontext.regs.UCreg_24);
175 err |= __put_user(regs->UCreg_25, &sf->uc.uc_mcontext.regs.UCreg_25);
176 err |= __put_user(regs->UCreg_26, &sf->uc.uc_mcontext.regs.UCreg_26);
177 err |= __put_user(regs->UCreg_fp, &sf->uc.uc_mcontext.regs.UCreg_fp);
178 err |= __put_user(regs->UCreg_ip, &sf->uc.uc_mcontext.regs.UCreg_ip);
179 err |= __put_user(regs->UCreg_sp, &sf->uc.uc_mcontext.regs.UCreg_sp);
180 err |= __put_user(regs->UCreg_lr, &sf->uc.uc_mcontext.regs.UCreg_lr);
181 err |= __put_user(regs->UCreg_pc, &sf->uc.uc_mcontext.regs.UCreg_pc);
182 err |= __put_user(regs->UCreg_asr, &sf->uc.uc_mcontext.regs.UCreg_asr);
183
184 err |= __put_user(current->thread.trap_no,
185 &sf->uc.uc_mcontext.trap_no);
186 err |= __put_user(current->thread.error_code,
187 &sf->uc.uc_mcontext.error_code);
188 err |= __put_user(current->thread.address,
189 &sf->uc.uc_mcontext.fault_address);
190 err |= __put_user(set->sig[0], &sf->uc.uc_mcontext.oldmask);
191
192 err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
193
194 return err;
195}
196
197static inline void __user *get_sigframe(struct k_sigaction *ka,
198 struct pt_regs *regs, int framesize)
199{
200 unsigned long sp = regs->UCreg_sp;
201 void __user *frame;
202
203 /*
204 * This is the X/Open sanctioned signal stack switching.
205 */
206 if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
207 sp = current->sas_ss_sp + current->sas_ss_size;
208
209 /*
210 * ATPCS B01 mandates 8-byte alignment
211 */
212 frame = (void __user *)((sp - framesize) & ~7);
213
214 /*
215 * Check that we can actually write to the signal frame.
216 */
217 if (!access_ok(VERIFY_WRITE, frame, framesize))
218 frame = NULL;
219
220 return frame;
221}
222
223static int setup_return(struct pt_regs *regs, struct k_sigaction *ka,
224 unsigned long __user *rc, void __user *frame, int usig)
225{
226 unsigned long handler = (unsigned long)ka->sa.sa_handler;
227 unsigned long retcode;
228 unsigned long asr = regs->UCreg_asr & ~PSR_f;
229
230 unsigned int idx = 0;
231
232 if (ka->sa.sa_flags & SA_SIGINFO)
233 idx += 1;
234
235 if (__put_user(sigreturn_codes[idx], rc) ||
236 __put_user(sigreturn_codes[idx+1], rc+1))
237 return 1;
238
239 retcode = KERN_SIGRETURN_CODE + (idx << 2);
240
241 regs->UCreg_00 = usig;
242 regs->UCreg_sp = (unsigned long)frame;
243 regs->UCreg_lr = retcode;
244 regs->UCreg_pc = handler;
245 regs->UCreg_asr = asr;
246
247 return 0;
248}
249
250static int setup_frame(int usig, struct k_sigaction *ka,
251 sigset_t *set, struct pt_regs *regs)
252{
253 struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
254 int err = 0;
255
256 if (!frame)
257 return 1;
258
259 /*
260 * Set uc.uc_flags to a value which sc.trap_no would never have.
261 */
262 err |= __put_user(0x5ac3c35a, &frame->uc.uc_flags);
263
264 err |= setup_sigframe(frame, regs, set);
265 if (err == 0)
266 err |= setup_return(regs, ka, frame->retcode, frame, usig);
267
268 return err;
269}
270
271static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
272 sigset_t *set, struct pt_regs *regs)
273{
274 struct rt_sigframe __user *frame =
275 get_sigframe(ka, regs, sizeof(*frame));
276 stack_t stack;
277 int err = 0;
278
279 if (!frame)
280 return 1;
281
282 err |= copy_siginfo_to_user(&frame->info, info);
283
284 err |= __put_user(0, &frame->sig.uc.uc_flags);
285 err |= __put_user(NULL, &frame->sig.uc.uc_link);
286
287 memset(&stack, 0, sizeof(stack));
288 stack.ss_sp = (void __user *)current->sas_ss_sp;
289 stack.ss_flags = sas_ss_flags(regs->UCreg_sp);
290 stack.ss_size = current->sas_ss_size;
291 err |= __copy_to_user(&frame->sig.uc.uc_stack, &stack, sizeof(stack));
292
293 err |= setup_sigframe(&frame->sig, regs, set);
294 if (err == 0)
295 err |= setup_return(regs, ka, frame->sig.retcode, frame, usig);
296
297 if (err == 0) {
298 /*
299 * For realtime signals we must also set the second and third
300 * arguments for the signal handler.
301 */
302 regs->UCreg_01 = (unsigned long)&frame->info;
303 regs->UCreg_02 = (unsigned long)&frame->sig.uc;
304 }
305
306 return err;
307}
308
309static inline void setup_syscall_restart(struct pt_regs *regs)
310{
311 regs->UCreg_00 = regs->UCreg_ORIG_00;
312 regs->UCreg_pc -= 4;
313}
314
315/*
316 * OK, we're invoking a handler
317 */
318static int handle_signal(unsigned long sig, struct k_sigaction *ka,
319 siginfo_t *info, sigset_t *oldset,
320 struct pt_regs *regs, int syscall)
321{
322 struct thread_info *thread = current_thread_info();
323 struct task_struct *tsk = current;
324 int usig = sig;
325 int ret;
326
327 /*
328 * If we were from a system call, check for system call restarting...
329 */
330 if (syscall) {
331 switch (regs->UCreg_00) {
332 case -ERESTART_RESTARTBLOCK:
333 case -ERESTARTNOHAND:
334 regs->UCreg_00 = -EINTR;
335 break;
336 case -ERESTARTSYS:
337 if (!(ka->sa.sa_flags & SA_RESTART)) {
338 regs->UCreg_00 = -EINTR;
339 break;
340 }
341 /* fallthrough */
342 case -ERESTARTNOINTR:
343 setup_syscall_restart(regs);
344 }
345 }
346
347 /*
348 * translate the signal
349 */
350 if (usig < 32 && thread->exec_domain
351 && thread->exec_domain->signal_invmap)
352 usig = thread->exec_domain->signal_invmap[usig];
353
354 /*
355 * Set up the stack frame
356 */
357 if (ka->sa.sa_flags & SA_SIGINFO)
358 ret = setup_rt_frame(usig, ka, info, oldset, regs);
359 else
360 ret = setup_frame(usig, ka, oldset, regs);
361
362 /*
363 * Check that the resulting registers are actually sane.
364 */
365 ret |= !valid_user_regs(regs);
366
367 if (ret != 0) {
368 force_sigsegv(sig, tsk);
369 return ret;
370 }
371
372 /*
373 * Block the signal if we were successful.
374 */
375 spin_lock_irq(&tsk->sighand->siglock);
376 sigorsets(&tsk->blocked, &tsk->blocked,
377 &ka->sa.sa_mask);
378 if (!(ka->sa.sa_flags & SA_NODEFER))
379 sigaddset(&tsk->blocked, sig);
380 recalc_sigpending();
381 spin_unlock_irq(&tsk->sighand->siglock);
382
383 return 0;
384}
385
386/*
387 * Note that 'init' is a special process: it doesn't get signals it doesn't
388 * want to handle. Thus you cannot kill init even with a SIGKILL even by
389 * mistake.
390 *
391 * Note that we go through the signals twice: once to check the signals that
392 * the kernel can handle, and then we build all the user-level signal handling
393 * stack-frames in one go after that.
394 */
395static void do_signal(struct pt_regs *regs, int syscall)
396{
397 struct k_sigaction ka;
398 siginfo_t info;
399 int signr;
400
401 /*
402 * We want the common case to go fast, which
403 * is why we may in certain cases get here from
404 * kernel mode. Just return without doing anything
405 * if so.
406 */
407 if (!user_mode(regs))
408 return;
409
410 if (try_to_freeze())
411 goto no_signal;
412
413 signr = get_signal_to_deliver(&info, &ka, regs, NULL);
414 if (signr > 0) {
415 sigset_t *oldset;
416
417 if (test_thread_flag(TIF_RESTORE_SIGMASK))
418 oldset = &current->saved_sigmask;
419 else
420 oldset = &current->blocked;
421 if (handle_signal(signr, &ka, &info, oldset, regs, syscall)
422 == 0) {
423 /*
424 * A signal was successfully delivered; the saved
425 * sigmask will have been stored in the signal frame,
426 * and will be restored by sigreturn, so we can simply
427 * clear the TIF_RESTORE_SIGMASK flag.
428 */
429 if (test_thread_flag(TIF_RESTORE_SIGMASK))
430 clear_thread_flag(TIF_RESTORE_SIGMASK);
431 }
432 return;
433 }
434
435 no_signal:
436 /*
437 * No signal to deliver to the process - restart the syscall.
438 */
439 if (syscall) {
440 if (regs->UCreg_00 == -ERESTART_RESTARTBLOCK) {
441 u32 __user *usp;
442
443 regs->UCreg_sp -= 4;
444 usp = (u32 __user *)regs->UCreg_sp;
445
446 if (put_user(regs->UCreg_pc, usp) == 0) {
447 regs->UCreg_pc = KERN_RESTART_CODE;
448 } else {
449 regs->UCreg_sp += 4;
450 force_sigsegv(0, current);
451 }
452 }
453 if (regs->UCreg_00 == -ERESTARTNOHAND ||
454 regs->UCreg_00 == -ERESTARTSYS ||
455 regs->UCreg_00 == -ERESTARTNOINTR) {
456 setup_syscall_restart(regs);
457 }
458
459 /* If there's no signal to deliver, we just put the saved
460 * sigmask back.
461 */
462 if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
463 clear_thread_flag(TIF_RESTORE_SIGMASK);
464 sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
465 }
466 }
467}
468
469asmlinkage void do_notify_resume(struct pt_regs *regs,
470 unsigned int thread_flags, int syscall)
471{
472 if (thread_flags & _TIF_SIGPENDING)
473 do_signal(regs, syscall);
474
475 if (thread_flags & _TIF_NOTIFY_RESUME) {
476 clear_thread_flag(TIF_NOTIFY_RESUME);
477 tracehook_notify_resume(regs);
478 if (current->replacement_session_keyring)
479 key_replace_session_keyring();
480 }
481}
482
483/*
484 * Copy signal return handlers into the vector page, and
485 * set sigreturn to be a pointer to these.
486 */
487void __init early_signal_init(void)
488{
489 memcpy((void *)kuser_vecpage_to_vectors(KERN_SIGRETURN_CODE),
490 sigreturn_codes, sizeof(sigreturn_codes));
491 memcpy((void *)kuser_vecpage_to_vectors(KERN_RESTART_CODE),
492 syscall_restart_code, sizeof(syscall_restart_code));
493 /* Need not to flush icache, since early_trap_init will do it last. */
494}
diff --git a/arch/unicore32/kernel/sleep.S b/arch/unicore32/kernel/sleep.S
new file mode 100644
index 00000000000..607a104aec5
--- /dev/null
+++ b/arch/unicore32/kernel/sleep.S
@@ -0,0 +1,202 @@
1/*
2 * linux/arch/unicore32/kernel/sleep.S
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/linkage.h>
15#include <asm/assembler.h>
16#include <mach/hardware.h>
17
18 .text
19
20pkunity_cpu_save_cp:
21
22 @ get coprocessor registers
23
24 movc r3, p0.c7, #0 @ PID
25 movc r4, p0.c2, #0 @ translation table base addr
26 movc r5, p0.c1, #0 @ control reg
27
28
29 @ store them plus current virtual stack ptr on stack
30 mov r6, sp
31 stm.w (r3 - r6), [sp-]
32
33 mov pc, lr
34
35pkunity_cpu_save_sp:
36 @ preserve phys address of stack
37 mov r0, sp
38 stw.w lr, [sp+], #-4
39 b.l sleep_phys_sp
40 ldw r1, =sleep_save_sp
41 stw r0, [r1]
42 ldw.w pc, [sp]+, #4
43
44/*
45 * puv3_cpu_suspend()
46 *
47 * Forces CPU into sleep state.
48 *
49 * r0 = value for PWRMODE M field for desired sleep state
50 */
51
52ENTRY(puv3_cpu_suspend)
53 stm.w (r16 - r27, lr), [sp-] @ save registers on stack
54 stm.w (r4 - r15), [sp-] @ save registers on stack
55
56#ifdef CONFIG_UNICORE_FPU_F64
57 sfm.w (f0 - f7 ), [sp-]
58 sfm.w (f8 - f15), [sp-]
59 sfm.w (f16 - f23), [sp-]
60 sfm.w (f24 - f31), [sp-]
61 cff r4, s31
62 stm.w (r4), [sp-]
63#endif
64 b.l pkunity_cpu_save_cp
65
66 b.l pkunity_cpu_save_sp
67
68 @ clean data cache
69 mov r1, #0
70 movc p0.c5, r1, #14
71 nop
72 nop
73 nop
74 nop
75
76
77
78 @ DDR2 BaseAddr
79 ldw r0, =(PKUNITY_DDR2CTRL_BASE)
80
81 @ PM BaseAddr
82 ldw r1, =(PKUNITY_PM_BASE)
83
84 @ set PLL_SYS_CFG reg, 275
85 movl r6, #0x00002401
86 stw r6, [r1+], #0x18
87 @ set PLL_DDR_CFG reg, 66MHz
88 movl r6, #0x00100c00
89 stw r6, [r1+], #0x1c
90
91 @ set wake up source
92 movl r8, #0x800001ff @ epip4d
93 stw r8, [r1+], #0xc
94
95 @ set PGSR
96 movl r5, #0x40000
97 stw r5, [r1+], #0x10
98
99 @ prepare DDR2 refresh settings
100 ldw r5, [r0+], #0x24
101 or r5, r5, #0x00000001
102
103 @ prepare PMCR for PLL changing
104 movl r6, #0xc
105
106 @ prepare for closing PLL
107 movl r7, #0x1
108
109 @ prepare sleep mode
110 mov r8, #0x1
111
112@ movl r0, 0x11111111
113@ put_word_ocd r0
114 b pkunity_cpu_do_suspend
115
116 .ltorg
117 .align 5
118pkunity_cpu_do_suspend:
119 b 101f
120 @ put DDR2 into self-refresh
121100: stw r5, [r0+], #0x24
122 @ change PLL
123 stw r6, [r1]
124 b 1f
125
126 .ltorg
127 .align 5
128101: b 102f
129 @ wait for PLL changing complete
1301: ldw r6, [r1+], #0x44
131 csub.a r6, #0x1
132 bne 1b
133 b 2f
134
135 .ltorg
136 .align 5
137102: b 100b
138 @ close PLL
1392: stw r7, [r1+], #0x4
140 @ enter sleep mode
141 stw r8, [r1]
1423: b 3b
143
144
145
146
147/*
148 * puv3_cpu_resume()
149 *
150 * entry point from bootloader into kernel during resume
151 *
152 * Note: Yes, part of the following code is located into the .data section.
153 * This is to allow sleep_save_sp to be accessed with a relative load
154 * while we can't rely on any MMU translation. We could have put
155 * sleep_save_sp in the .text section as well, but some setups might
156 * insist on it to be truly read-only.
157 */
158
159 .data
160 .align 5
161ENTRY(puv3_cpu_resume)
162@ movl r0, 0x20202020
163@ put_word_ocd r0
164
165 ldw r0, sleep_save_sp @ stack phys addr
166 ldw r2, =resume_after_mmu @ its absolute virtual address
167 ldm (r3 - r6), [r0]+ @ CP regs + virt stack ptr
168 mov sp, r6 @ CP regs + virt stack ptr
169
170 mov r1, #0
171 movc p0.c6, r1, #6 @ invalidate I & D TLBs
172 movc p0.c5, r1, #28 @ invalidate I & D caches, BTB
173
174 movc p0.c7, r3, #0 @ PID
175 movc p0.c2, r4, #0 @ translation table base addr
176 movc p0.c1, r5, #0 @ control reg, turn on mmu
177 nop
178 jump r2
179 nop
180 nop
181 nop
182 nop
183 nop
184
185sleep_save_sp:
186 .word 0 @ preserve stack phys ptr here
187
188 .text
189resume_after_mmu:
190@ movl r0, 0x30303030
191@ put_word_ocd r0
192
193#ifdef CONFIG_UNICORE_FPU_F64
194 lfm.w (f0 - f7 ), [sp]+
195 lfm.w (f8 - f15), [sp]+
196 lfm.w (f16 - f23), [sp]+
197 lfm.w (f24 - f31), [sp]+
198 ldm.w (r4), [sp]+
199 ctf r4, s31
200#endif
201 ldm.w (r4 - r15), [sp]+ @ restore registers from stack
202 ldm.w (r16 - r27, pc), [sp]+ @ return to caller
diff --git a/arch/unicore32/kernel/stacktrace.c b/arch/unicore32/kernel/stacktrace.c
new file mode 100644
index 00000000000..b34030bdabe
--- /dev/null
+++ b/arch/unicore32/kernel/stacktrace.c
@@ -0,0 +1,131 @@
1/*
2 * linux/arch/unicore32/kernel/stacktrace.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/sched.h>
14#include <linux/stacktrace.h>
15
16#include <asm/stacktrace.h>
17
18#if defined(CONFIG_FRAME_POINTER)
19/*
20 * Unwind the current stack frame and store the new register values in the
21 * structure passed as argument. Unwinding is equivalent to a function return,
22 * hence the new PC value rather than LR should be used for backtrace.
23 *
24 * With framepointer enabled, a simple function prologue looks like this:
25 * mov ip, sp
26 * stmdb sp!, {fp, ip, lr, pc}
27 * sub fp, ip, #4
28 *
29 * A simple function epilogue looks like this:
30 * ldm sp, {fp, sp, pc}
31 *
32 * Note that with framepointer enabled, even the leaf functions have the same
33 * prologue and epilogue, therefore we can ignore the LR value in this case.
34 */
35int notrace unwind_frame(struct stackframe *frame)
36{
37 unsigned long high, low;
38 unsigned long fp = frame->fp;
39
40 /* only go to a higher address on the stack */
41 low = frame->sp;
42 high = ALIGN(low, THREAD_SIZE);
43
44 /* check current frame pointer is within bounds */
45 if (fp < (low + 12) || fp + 4 >= high)
46 return -EINVAL;
47
48 /* restore the registers from the stack frame */
49 frame->fp = *(unsigned long *)(fp - 12);
50 frame->sp = *(unsigned long *)(fp - 8);
51 frame->pc = *(unsigned long *)(fp - 4);
52
53 return 0;
54}
55#endif
56
57void notrace walk_stackframe(struct stackframe *frame,
58 int (*fn)(struct stackframe *, void *), void *data)
59{
60 while (1) {
61 int ret;
62
63 if (fn(frame, data))
64 break;
65 ret = unwind_frame(frame);
66 if (ret < 0)
67 break;
68 }
69}
70EXPORT_SYMBOL(walk_stackframe);
71
72#ifdef CONFIG_STACKTRACE
73struct stack_trace_data {
74 struct stack_trace *trace;
75 unsigned int no_sched_functions;
76 unsigned int skip;
77};
78
79static int save_trace(struct stackframe *frame, void *d)
80{
81 struct stack_trace_data *data = d;
82 struct stack_trace *trace = data->trace;
83 unsigned long addr = frame->pc;
84
85 if (data->no_sched_functions && in_sched_functions(addr))
86 return 0;
87 if (data->skip) {
88 data->skip--;
89 return 0;
90 }
91
92 trace->entries[trace->nr_entries++] = addr;
93
94 return trace->nr_entries >= trace->max_entries;
95}
96
97void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
98{
99 struct stack_trace_data data;
100 struct stackframe frame;
101
102 data.trace = trace;
103 data.skip = trace->skip;
104
105 if (tsk != current) {
106 data.no_sched_functions = 1;
107 frame.fp = thread_saved_fp(tsk);
108 frame.sp = thread_saved_sp(tsk);
109 frame.lr = 0; /* recovered from the stack */
110 frame.pc = thread_saved_pc(tsk);
111 } else {
112 register unsigned long current_sp asm("sp");
113
114 data.no_sched_functions = 0;
115 frame.fp = (unsigned long)__builtin_frame_address(0);
116 frame.sp = current_sp;
117 frame.lr = (unsigned long)__builtin_return_address(0);
118 frame.pc = (unsigned long)save_stack_trace_tsk;
119 }
120
121 walk_stackframe(&frame, save_trace, &data);
122 if (trace->nr_entries < trace->max_entries)
123 trace->entries[trace->nr_entries++] = ULONG_MAX;
124}
125
126void save_stack_trace(struct stack_trace *trace)
127{
128 save_stack_trace_tsk(current, trace);
129}
130EXPORT_SYMBOL_GPL(save_stack_trace);
131#endif
diff --git a/arch/unicore32/kernel/sys.c b/arch/unicore32/kernel/sys.c
new file mode 100644
index 00000000000..3afe60a39ac
--- /dev/null
+++ b/arch/unicore32/kernel/sys.c
@@ -0,0 +1,126 @@
1/*
2 * linux/arch/unicore32/kernel/sys.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12#include <linux/module.h>
13#include <linux/errno.h>
14#include <linux/sched.h>
15#include <linux/slab.h>
16#include <linux/mm.h>
17#include <linux/sem.h>
18#include <linux/msg.h>
19#include <linux/shm.h>
20#include <linux/stat.h>
21#include <linux/syscalls.h>
22#include <linux/mman.h>
23#include <linux/fs.h>
24#include <linux/file.h>
25#include <linux/ipc.h>
26#include <linux/uaccess.h>
27
28#include <asm/syscalls.h>
29#include <asm/cacheflush.h>
30
31/* Clone a task - this clones the calling program thread.
32 * This is called indirectly via a small wrapper
33 */
34asmlinkage long __sys_clone(unsigned long clone_flags, unsigned long newsp,
35 void __user *parent_tid, void __user *child_tid,
36 struct pt_regs *regs)
37{
38 if (!newsp)
39 newsp = regs->UCreg_sp;
40
41 return do_fork(clone_flags, newsp, regs, 0,
42 parent_tid, child_tid);
43}
44
45/* sys_execve() executes a new program.
46 * This is called indirectly via a small wrapper
47 */
48asmlinkage long __sys_execve(const char __user *filename,
49 const char __user *const __user *argv,
50 const char __user *const __user *envp,
51 struct pt_regs *regs)
52{
53 int error;
54 char *fn;
55
56 fn = getname(filename);
57 error = PTR_ERR(fn);
58 if (IS_ERR(fn))
59 goto out;
60 error = do_execve(fn, argv, envp, regs);
61 putname(fn);
62out:
63 return error;
64}
65
66int kernel_execve(const char *filename,
67 const char *const argv[],
68 const char *const envp[])
69{
70 struct pt_regs regs;
71 int ret;
72
73 memset(&regs, 0, sizeof(struct pt_regs));
74 ret = do_execve(filename,
75 (const char __user *const __user *)argv,
76 (const char __user *const __user *)envp, &regs);
77 if (ret < 0)
78 goto out;
79
80 /*
81 * Save argc to the register structure for userspace.
82 */
83 regs.UCreg_00 = ret;
84
85 /*
86 * We were successful. We won't be returning to our caller, but
87 * instead to user space by manipulating the kernel stack.
88 */
89 asm("add r0, %0, %1\n\t"
90 "mov r1, %2\n\t"
91 "mov r2, %3\n\t"
92 "mov r22, #0\n\t" /* not a syscall */
93 "mov r23, %0\n\t" /* thread structure */
94 "b.l memmove\n\t" /* copy regs to top of stack */
95 "mov sp, r0\n\t" /* reposition stack pointer */
96 "b ret_to_user"
97 :
98 : "r" (current_thread_info()),
99 "Ir" (THREAD_START_SP - sizeof(regs)),
100 "r" (&regs),
101 "Ir" (sizeof(regs))
102 : "r0", "r1", "r2", "r3", "ip", "lr", "memory");
103
104 out:
105 return ret;
106}
107EXPORT_SYMBOL(kernel_execve);
108
109/* Note: used by the compat code even in 64-bit Linux. */
110SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len,
111 unsigned long, prot, unsigned long, flags,
112 unsigned long, fd, unsigned long, off_4k)
113{
114 return sys_mmap_pgoff(addr, len, prot, flags, fd,
115 off_4k);
116}
117
118/* Provide the actual syscall number to call mapping. */
119#undef __SYSCALL
120#define __SYSCALL(nr, call) [nr] = (call),
121
122/* Note that we don't include <linux/unistd.h> but <asm/unistd.h> */
123void *sys_call_table[__NR_syscalls] = {
124 [0 ... __NR_syscalls-1] = sys_ni_syscall,
125#include <asm/unistd.h>
126};
diff --git a/arch/unicore32/kernel/time.c b/arch/unicore32/kernel/time.c
new file mode 100644
index 00000000000..080710c0924
--- /dev/null
+++ b/arch/unicore32/kernel/time.c
@@ -0,0 +1,143 @@
1/*
2 * linux/arch/unicore32/kernel/time.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13#include <linux/init.h>
14#include <linux/errno.h>
15#include <linux/interrupt.h>
16#include <linux/irq.h>
17#include <linux/timex.h>
18#include <linux/clockchips.h>
19
20#include <mach/hardware.h>
21
22#define MIN_OSCR_DELTA 2
23
24static irqreturn_t puv3_ost0_interrupt(int irq, void *dev_id)
25{
26 struct clock_event_device *c = dev_id;
27
28 /* Disarm the compare/match, signal the event. */
29 writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER);
30 writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR);
31 c->event_handler(c);
32
33 return IRQ_HANDLED;
34}
35
36static int
37puv3_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c)
38{
39 unsigned long next, oscr;
40
41 writel(readl(OST_OIER) | OST_OIER_E0, OST_OIER);
42 next = readl(OST_OSCR) + delta;
43 writel(next, OST_OSMR0);
44 oscr = readl(OST_OSCR);
45
46 return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
47}
48
49static void
50puv3_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c)
51{
52 switch (mode) {
53 case CLOCK_EVT_MODE_ONESHOT:
54 case CLOCK_EVT_MODE_UNUSED:
55 case CLOCK_EVT_MODE_SHUTDOWN:
56 writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER);
57 writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR);
58 break;
59
60 case CLOCK_EVT_MODE_RESUME:
61 case CLOCK_EVT_MODE_PERIODIC:
62 break;
63 }
64}
65
66static struct clock_event_device ckevt_puv3_osmr0 = {
67 .name = "osmr0",
68 .features = CLOCK_EVT_FEAT_ONESHOT,
69 .rating = 200,
70 .set_next_event = puv3_osmr0_set_next_event,
71 .set_mode = puv3_osmr0_set_mode,
72};
73
74static cycle_t puv3_read_oscr(struct clocksource *cs)
75{
76 return readl(OST_OSCR);
77}
78
79static struct clocksource cksrc_puv3_oscr = {
80 .name = "oscr",
81 .rating = 200,
82 .read = puv3_read_oscr,
83 .mask = CLOCKSOURCE_MASK(32),
84 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
85};
86
87static struct irqaction puv3_timer_irq = {
88 .name = "ost0",
89 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
90 .handler = puv3_ost0_interrupt,
91 .dev_id = &ckevt_puv3_osmr0,
92};
93
94void __init time_init(void)
95{
96 writel(0, OST_OIER); /* disable any timer interrupts */
97 writel(0, OST_OSSR); /* clear status on all timers */
98
99 clockevents_calc_mult_shift(&ckevt_puv3_osmr0, CLOCK_TICK_RATE, 5);
100
101 ckevt_puv3_osmr0.max_delta_ns =
102 clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0);
103 ckevt_puv3_osmr0.min_delta_ns =
104 clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1;
105 ckevt_puv3_osmr0.cpumask = cpumask_of(0);
106
107 setup_irq(IRQ_TIMER0, &puv3_timer_irq);
108
109 clocksource_register_hz(&cksrc_puv3_oscr, CLOCK_TICK_RATE);
110 clockevents_register_device(&ckevt_puv3_osmr0);
111}
112
113#ifdef CONFIG_PM
114unsigned long osmr[4], oier;
115
116void puv3_timer_suspend(void)
117{
118 osmr[0] = readl(OST_OSMR0);
119 osmr[1] = readl(OST_OSMR1);
120 osmr[2] = readl(OST_OSMR2);
121 osmr[3] = readl(OST_OSMR3);
122 oier = readl(OST_OIER);
123}
124
125void puv3_timer_resume(void)
126{
127 writel(0, OST_OSSR);
128 writel(osmr[0], OST_OSMR0);
129 writel(osmr[1], OST_OSMR1);
130 writel(osmr[2], OST_OSMR2);
131 writel(osmr[3], OST_OSMR3);
132 writel(oier, OST_OIER);
133
134 /*
135 * OSMR0 is the system timer: make sure OSCR is sufficiently behind
136 */
137 writel(readl(OST_OSMR0) - LATCH, OST_OSCR);
138}
139#else
140void puv3_timer_suspend(void) { };
141void puv3_timer_resume(void) { };
142#endif
143
diff --git a/arch/unicore32/kernel/traps.c b/arch/unicore32/kernel/traps.c
new file mode 100644
index 00000000000..25abbb10172
--- /dev/null
+++ b/arch/unicore32/kernel/traps.c
@@ -0,0 +1,333 @@
1/*
2 * linux/arch/unicore32/kernel/traps.c
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * 'traps.c' handles hardware exceptions after we have saved some state.
13 * Mostly a debugging aid, but will probably kill the offending process.
14 */
15#include <linux/module.h>
16#include <linux/signal.h>
17#include <linux/spinlock.h>
18#include <linux/personality.h>
19#include <linux/kallsyms.h>
20#include <linux/kdebug.h>
21#include <linux/uaccess.h>
22#include <linux/delay.h>
23#include <linux/hardirq.h>
24#include <linux/init.h>
25#include <linux/uaccess.h>
26#include <linux/atomic.h>
27#include <linux/unistd.h>
28
29#include <asm/cacheflush.h>
30#include <asm/system.h>
31#include <asm/traps.h>
32
33#include "setup.h"
34
35static void dump_mem(const char *, const char *, unsigned long, unsigned long);
36
37void dump_backtrace_entry(unsigned long where,
38 unsigned long from, unsigned long frame)
39{
40#ifdef CONFIG_KALLSYMS
41 printk(KERN_DEFAULT "[<%08lx>] (%pS) from [<%08lx>] (%pS)\n",
42 where, (void *)where, from, (void *)from);
43#else
44 printk(KERN_DEFAULT "Function entered at [<%08lx>] from [<%08lx>]\n",
45 where, from);
46#endif
47}
48
49/*
50 * Stack pointers should always be within the kernels view of
51 * physical memory. If it is not there, then we can't dump
52 * out any information relating to the stack.
53 */
54static int verify_stack(unsigned long sp)
55{
56 if (sp < PAGE_OFFSET ||
57 (sp > (unsigned long)high_memory && high_memory != NULL))
58 return -EFAULT;
59
60 return 0;
61}
62
63/*
64 * Dump out the contents of some memory nicely...
65 */
66static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
67 unsigned long top)
68{
69 unsigned long first;
70 mm_segment_t fs;
71 int i;
72
73 /*
74 * We need to switch to kernel mode so that we can use __get_user
75 * to safely read from kernel space. Note that we now dump the
76 * code first, just in case the backtrace kills us.
77 */
78 fs = get_fs();
79 set_fs(KERNEL_DS);
80
81 printk(KERN_DEFAULT "%s%s(0x%08lx to 0x%08lx)\n",
82 lvl, str, bottom, top);
83
84 for (first = bottom & ~31; first < top; first += 32) {
85 unsigned long p;
86 char str[sizeof(" 12345678") * 8 + 1];
87
88 memset(str, ' ', sizeof(str));
89 str[sizeof(str) - 1] = '\0';
90
91 for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {
92 if (p >= bottom && p < top) {
93 unsigned long val;
94 if (__get_user(val, (unsigned long *)p) == 0)
95 sprintf(str + i * 9, " %08lx", val);
96 else
97 sprintf(str + i * 9, " ????????");
98 }
99 }
100 printk(KERN_DEFAULT "%s%04lx:%s\n", lvl, first & 0xffff, str);
101 }
102
103 set_fs(fs);
104}
105
106static void dump_instr(const char *lvl, struct pt_regs *regs)
107{
108 unsigned long addr = instruction_pointer(regs);
109 const int width = 8;
110 mm_segment_t fs;
111 char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
112 int i;
113
114 /*
115 * We need to switch to kernel mode so that we can use __get_user
116 * to safely read from kernel space. Note that we now dump the
117 * code first, just in case the backtrace kills us.
118 */
119 fs = get_fs();
120 set_fs(KERNEL_DS);
121
122 for (i = -4; i < 1; i++) {
123 unsigned int val, bad;
124
125 bad = __get_user(val, &((u32 *)addr)[i]);
126
127 if (!bad)
128 p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
129 width, val);
130 else {
131 p += sprintf(p, "bad PC value");
132 break;
133 }
134 }
135 printk(KERN_DEFAULT "%sCode: %s\n", lvl, str);
136
137 set_fs(fs);
138}
139
140static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
141{
142 unsigned int fp, mode;
143 int ok = 1;
144
145 printk(KERN_DEFAULT "Backtrace: ");
146
147 if (!tsk)
148 tsk = current;
149
150 if (regs) {
151 fp = regs->UCreg_fp;
152 mode = processor_mode(regs);
153 } else if (tsk != current) {
154 fp = thread_saved_fp(tsk);
155 mode = 0x10;
156 } else {
157 asm("mov %0, fp" : "=r" (fp) : : "cc");
158 mode = 0x10;
159 }
160
161 if (!fp) {
162 printk("no frame pointer");
163 ok = 0;
164 } else if (verify_stack(fp)) {
165 printk("invalid frame pointer 0x%08x", fp);
166 ok = 0;
167 } else if (fp < (unsigned long)end_of_stack(tsk))
168 printk("frame pointer underflow");
169 printk("\n");
170
171 if (ok)
172 c_backtrace(fp, mode);
173}
174
175void dump_stack(void)
176{
177 dump_backtrace(NULL, NULL);
178}
179EXPORT_SYMBOL(dump_stack);
180
181void show_stack(struct task_struct *tsk, unsigned long *sp)
182{
183 dump_backtrace(NULL, tsk);
184 barrier();
185}
186
187static int __die(const char *str, int err, struct thread_info *thread,
188 struct pt_regs *regs)
189{
190 struct task_struct *tsk = thread->task;
191 static int die_counter;
192 int ret;
193
194 printk(KERN_EMERG "Internal error: %s: %x [#%d]\n",
195 str, err, ++die_counter);
196 sysfs_printk_last_file();
197
198 /* trap and error numbers are mostly meaningless on UniCore */
199 ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, \
200 SIGSEGV);
201 if (ret == NOTIFY_STOP)
202 return ret;
203
204 print_modules();
205 __show_regs(regs);
206 printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n",
207 TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
208
209 if (!user_mode(regs) || in_interrupt()) {
210 dump_mem(KERN_EMERG, "Stack: ", regs->UCreg_sp,
211 THREAD_SIZE + (unsigned long)task_stack_page(tsk));
212 dump_backtrace(regs, tsk);
213 dump_instr(KERN_EMERG, regs);
214 }
215
216 return ret;
217}
218
219DEFINE_SPINLOCK(die_lock);
220
221/*
222 * This function is protected against re-entrancy.
223 */
224void die(const char *str, struct pt_regs *regs, int err)
225{
226 struct thread_info *thread = current_thread_info();
227 int ret;
228
229 oops_enter();
230
231 spin_lock_irq(&die_lock);
232 console_verbose();
233 bust_spinlocks(1);
234 ret = __die(str, err, thread, regs);
235
236 bust_spinlocks(0);
237 add_taint(TAINT_DIE);
238 spin_unlock_irq(&die_lock);
239 oops_exit();
240
241 if (in_interrupt())
242 panic("Fatal exception in interrupt");
243 if (panic_on_oops)
244 panic("Fatal exception");
245 if (ret != NOTIFY_STOP)
246 do_exit(SIGSEGV);
247}
248
249void uc32_notify_die(const char *str, struct pt_regs *regs,
250 struct siginfo *info, unsigned long err, unsigned long trap)
251{
252 if (user_mode(regs)) {
253 current->thread.error_code = err;
254 current->thread.trap_no = trap;
255
256 force_sig_info(info->si_signo, info, current);
257 } else
258 die(str, regs, err);
259}
260
261/*
262 * bad_mode handles the impossible case in the vectors. If you see one of
263 * these, then it's extremely serious, and could mean you have buggy hardware.
264 * It never returns, and never tries to sync. We hope that we can at least
265 * dump out some state information...
266 */
267asmlinkage void bad_mode(struct pt_regs *regs, unsigned int reason)
268{
269 console_verbose();
270
271 printk(KERN_CRIT "Bad mode detected with reason 0x%x\n", reason);
272
273 die("Oops - bad mode", regs, 0);
274 local_irq_disable();
275 panic("bad mode");
276}
277
278void __pte_error(const char *file, int line, unsigned long val)
279{
280 printk(KERN_DEFAULT "%s:%d: bad pte %08lx.\n", file, line, val);
281}
282
283void __pmd_error(const char *file, int line, unsigned long val)
284{
285 printk(KERN_DEFAULT "%s:%d: bad pmd %08lx.\n", file, line, val);
286}
287
288void __pgd_error(const char *file, int line, unsigned long val)
289{
290 printk(KERN_DEFAULT "%s:%d: bad pgd %08lx.\n", file, line, val);
291}
292
293asmlinkage void __div0(void)
294{
295 printk(KERN_DEFAULT "Division by zero in kernel.\n");
296 dump_stack();
297}
298EXPORT_SYMBOL(__div0);
299
300void abort(void)
301{
302 BUG();
303
304 /* if that doesn't kill us, halt */
305 panic("Oops failed to kill thread");
306}
307EXPORT_SYMBOL(abort);
308
309void __init trap_init(void)
310{
311 return;
312}
313
314void __init early_trap_init(void)
315{
316 unsigned long vectors = VECTORS_BASE;
317
318 /*
319 * Copy the vectors, stubs (in entry-unicore.S)
320 * into the vector page, mapped at 0xffff0000, and ensure these
321 * are visible to the instruction stream.
322 */
323 memcpy((void *)vectors,
324 __vectors_start,
325 __vectors_end - __vectors_start);
326 memcpy((void *)vectors + 0x200,
327 __stubs_start,
328 __stubs_end - __stubs_start);
329
330 early_signal_init();
331
332 flush_icache_range(vectors, vectors + PAGE_SIZE);
333}
diff --git a/arch/unicore32/kernel/vmlinux.lds.S b/arch/unicore32/kernel/vmlinux.lds.S
new file mode 100644
index 00000000000..0b4eb89729e
--- /dev/null
+++ b/arch/unicore32/kernel/vmlinux.lds.S
@@ -0,0 +1,61 @@
1/*
2 * linux/arch/unicore32/kernel/vmlinux.lds.S
3 *
4 * Code specific to PKUnity SoC and UniCore ISA
5 *
6 * Copyright (C) 2001-2010 GUAN Xue-tao
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <asm-generic/vmlinux.lds.h>
14#include <asm/thread_info.h>
15#include <asm/memory.h>
16#include <asm/page.h>
17
18OUTPUT_ARCH(unicore32)
19ENTRY(stext)
20
21jiffies = jiffies_64;
22
23SECTIONS
24{
25 . = PAGE_OFFSET + KERNEL_IMAGE_START;
26
27 _text = .;
28 __init_begin = .;
29 HEAD_TEXT_SECTION
30 INIT_TEXT_SECTION(PAGE_SIZE)
31 INIT_DATA_SECTION(16)
32 PERCPU(PAGE_SIZE)
33 __init_end = .;
34
35 _stext = .;
36 .text : { /* Real text segment */
37 TEXT_TEXT
38 SCHED_TEXT
39 LOCK_TEXT
40
41 *(.fixup)
42 *(.gnu.warning)
43 }
44 _etext = .;
45
46 _sdata = .;
47 RO_DATA_SECTION(PAGE_SIZE)
48 RW_DATA_SECTION(32, PAGE_SIZE, THREAD_SIZE)
49 _edata = .;
50
51 EXCEPTION_TABLE(32)
52 NOTES
53
54 BSS_SECTION(0, 0, 0)
55 _end = .;
56
57 STABS_DEBUG
58 DWARF_DEBUG
59
60 DISCARDS /* Exit code and data */
61}