diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-17 13:11:25 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-03-17 13:11:25 -0400 |
commit | 7b7adc4a016a1decb806eb71ecab98721fa7f146 (patch) | |
tree | 0a6f9a6e5659faa94604fbc575382a18f143c657 /arch/unicore32/kernel | |
parent | 31598e8713ef501c8f6aad2e2ec8a9457e8877c1 (diff) | |
parent | 289d6b0e287e0acd85f3e6b7ea6c2cb5c234909a (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')
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. | ||
6 | obj-y := dma.o elf.o entry.o process.o ptrace.o | ||
7 | obj-y += setup.o signal.o sys.o stacktrace.o traps.o | ||
8 | |||
9 | obj-$(CONFIG_MODULES) += ksyms.o module.o | ||
10 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | ||
11 | |||
12 | obj-$(CONFIG_CPU_FREQ) += cpu-ucv2.o | ||
13 | obj-$(CONFIG_UNICORE_FPU_F64) += fpu-ucf64.o | ||
14 | |||
15 | # obj-y for architecture PKUnity v3 | ||
16 | obj-$(CONFIG_ARCH_PUV3) += clock.o irq.o time.o | ||
17 | |||
18 | obj-$(CONFIG_PUV3_GPIO) += gpio.o | ||
19 | obj-$(CONFIG_PUV3_RTC) += rtc.o | ||
20 | obj-$(CONFIG_PUV3_PWM) += pwm.o | ||
21 | obj-$(CONFIG_PUV3_PM) += pm.o sleep.o | ||
22 | obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o | ||
23 | |||
24 | obj-$(CONFIG_PCI) += pci.o | ||
25 | |||
26 | # obj-y for specific machines | ||
27 | obj-$(CONFIG_ARCH_PUV3) += puv3-core.o | ||
28 | obj-$(CONFIG_PUV3_NB0916) += puv3-nb0916.o | ||
29 | |||
30 | head-y := head.o | ||
31 | obj-$(CONFIG_DEBUG_LL) += debug.o | ||
32 | |||
33 | extra-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 | |||
37 | int 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 | */ | ||
30 | struct clk { | ||
31 | struct list_head node; | ||
32 | unsigned long rate; | ||
33 | const char *name; | ||
34 | }; | ||
35 | |||
36 | static struct clk clk_ost_clk = { | ||
37 | .name = "OST_CLK", | ||
38 | .rate = CLOCK_TICK_RATE, | ||
39 | }; | ||
40 | |||
41 | static struct clk clk_mclk_clk = { | ||
42 | .name = "MAIN_CLK", | ||
43 | }; | ||
44 | |||
45 | static struct clk clk_bclk32_clk = { | ||
46 | .name = "BUS32_CLK", | ||
47 | }; | ||
48 | |||
49 | static struct clk clk_ddr_clk = { | ||
50 | .name = "DDR_CLK", | ||
51 | }; | ||
52 | |||
53 | static struct clk clk_vga_clk = { | ||
54 | .name = "VGA_CLK", | ||
55 | }; | ||
56 | |||
57 | static LIST_HEAD(clocks); | ||
58 | static DEFINE_MUTEX(clocks_mutex); | ||
59 | |||
60 | struct 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 | } | ||
75 | EXPORT_SYMBOL(clk_get); | ||
76 | |||
77 | void clk_put(struct clk *clk) | ||
78 | { | ||
79 | } | ||
80 | EXPORT_SYMBOL(clk_put); | ||
81 | |||
82 | int clk_enable(struct clk *clk) | ||
83 | { | ||
84 | return 0; | ||
85 | } | ||
86 | EXPORT_SYMBOL(clk_enable); | ||
87 | |||
88 | void clk_disable(struct clk *clk) | ||
89 | { | ||
90 | } | ||
91 | EXPORT_SYMBOL(clk_disable); | ||
92 | |||
93 | unsigned long clk_get_rate(struct clk *clk) | ||
94 | { | ||
95 | return clk->rate; | ||
96 | } | ||
97 | EXPORT_SYMBOL(clk_get_rate); | ||
98 | |||
99 | struct { | ||
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 | |||
117 | struct { | ||
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 | |||
136 | int 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 | } | ||
215 | EXPORT_SYMBOL(clk_set_rate); | ||
216 | |||
217 | int 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 | } | ||
226 | EXPORT_SYMBOL(clk_register); | ||
227 | |||
228 | void clk_unregister(struct clk *clk) | ||
229 | { | ||
230 | mutex_lock(&clocks_mutex); | ||
231 | list_del(&clk->node); | ||
232 | mutex_unlock(&clocks_mutex); | ||
233 | } | ||
234 | EXPORT_SYMBOL(clk_unregister); | ||
235 | |||
236 | struct { | ||
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 | |||
304 | struct { | ||
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 | |||
325 | static 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 | } | ||
390 | core_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 | |||
22 | static 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 | */ | ||
27 | int 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 | |||
38 | static 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 | |||
47 | static 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 | |||
68 | static 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 | |||
79 | static 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 | |||
88 | static int __init ucv2_cpufreq_init(void) | ||
89 | { | ||
90 | return cpufreq_register_driver(&ucv2_driver); | ||
91 | } | ||
92 | |||
93 | arch_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 | ||
18 | 1001: 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 | ||
78 | 1001: ldr \rd, [\rx, #UART_LSR_OFFSET] | ||
79 | tst \rd, #UART_LSR_THRE | ||
80 | beq 1001b | ||
81 | .endm | ||
82 | |||
83 | .macro busyuart,rd,rx | ||
84 | 1001: 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 | */ | ||
29 | ENTRY(printhex8) | ||
30 | mov r1, #8 | ||
31 | b printhex | ||
32 | ENDPROC(printhex8) | ||
33 | |||
34 | ENTRY(printhex4) | ||
35 | mov r1, #4 | ||
36 | b printhex | ||
37 | ENDPROC(printhex4) | ||
38 | |||
39 | ENTRY(printhex2) | ||
40 | mov r1, #2 | ||
41 | printhex: adr r2, hexbuf | ||
42 | add r3, r2, r1 | ||
43 | mov r1, #0 | ||
44 | stb r1, [r3] | ||
45 | 1: and r1, r0, #15 | ||
46 | mov r0, r0 >> #4 | ||
47 | csub.a r1, #10 | ||
48 | beg 2f | ||
49 | add r1, r1, #'0' - 'a' + 10 | ||
50 | 2: 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 | ||
56 | ENDPROC(printhex2) | ||
57 | |||
58 | .ltorg | ||
59 | |||
60 | ENTRY(printascii) | ||
61 | addruart r3 | ||
62 | b 2f | ||
63 | 1: waituart r2, r3 | ||
64 | senduart r1, r3 | ||
65 | busyuart r2, r3 | ||
66 | cxor.a r1, #'\n' | ||
67 | cmoveq r1, #'\r' | ||
68 | beq 1b | ||
69 | 2: cxor.a r0, #0 | ||
70 | beq 3f | ||
71 | ldb.w r1, [r0]+, #1 | ||
72 | cxor.a r1, #0 | ||
73 | bne 1b | ||
74 | 3: mov pc, lr | ||
75 | ENDPROC(printascii) | ||
76 | |||
77 | ENTRY(printch) | ||
78 | addruart r3 | ||
79 | mov r1, r0 | ||
80 | mov r0, #0 | ||
81 | b 1b | ||
82 | ENDPROC(printch) | ||
83 | |||
84 | hexbuf: .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 | |||
26 | struct 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 | |||
34 | static struct dma_channel dma_channels[MAX_DMA_CHANNELS]; | ||
35 | |||
36 | int 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 | } | ||
76 | EXPORT_SYMBOL(puv3_request_dma); | ||
77 | |||
78 | void 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 | } | ||
94 | EXPORT_SYMBOL(puv3_free_dma); | ||
95 | |||
96 | static 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 | |||
124 | static 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 | |||
152 | int __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 | |||
183 | postcore_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 | |||
19 | static 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 | |||
29 | static 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 */ | ||
37 | static struct console *early_console = &early_ocd_console; | ||
38 | |||
39 | static int __initdata keep_early; | ||
40 | |||
41 | static 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 | } | ||
59 | early_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 | |||
18 | int 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 | } | ||
30 | EXPORT_SYMBOL(elf_check_arch); | ||
31 | |||
32 | void elf_set_personality(const struct elf32_hdr *x) | ||
33 | { | ||
34 | unsigned int personality = PER_LINUX; | ||
35 | |||
36 | set_personality(personality); | ||
37 | } | ||
38 | EXPORT_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 | ||
101 | 1001: /* 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 | ||
121 | 901: .word 0, 0, 0, 0, 0 @ r0-r3, lr | ||
122 | 902: .asciz ": epip4d\n" | ||
123 | .align | ||
124 | 903: | ||
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 | */ | ||
136 | scno .req r21 @ syscall number | ||
137 | tbl .req r22 @ syscall table pointer | ||
138 | why .req r22 @ Linux syscall (!= 0) | ||
139 | tsk .req r23 @ current thread_info | ||
140 | |||
141 | /* | ||
142 | * Interrupt handling. Preserves r17, r18, r19 | ||
143 | */ | ||
144 | .macro intr_handler | ||
145 | 1: 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 | ||
153 | 2: | ||
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 | ||
250 | ENDPROC(__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 | ||
263 | 1: | ||
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 | ||
294 | ENDPROC(__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 | ||
309 | ENDPROC(__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 | ||
320 | ENDPROC(__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 | ||
333 | 1: | ||
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 | ||
357 | ENDPROC(__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 | @ | ||
382 | 1: 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 | ||
426 | 209: | ||
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 | ||
447 | ENDPROC(__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 | ||
459 | ENDPROC(__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 | ||
470 | ENDPROC(__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 | */ | ||
485 | ENTRY(ret_from_exception) | ||
486 | get_thread_info tsk | ||
487 | mov why, #0 | ||
488 | b ret_to_user | ||
489 | ENDPROC(__pabt_user) | ||
490 | ENDPROC(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 | */ | ||
497 | ENTRY(__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 | ||
522 | ENDPROC(__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 | */ | ||
530 | ret_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 | */ | ||
542 | fast_work_pending: | ||
543 | stw.w r0, [sp+], #S_R0+S_OFF @ returned r0 | ||
544 | work_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 | |||
556 | work_resched: | ||
557 | b.l schedule | ||
558 | /* | ||
559 | * "slow" syscall return path. "why" tells us if this was a real syscall. | ||
560 | */ | ||
561 | ENTRY(ret_to_user) | ||
562 | ret_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 | ||
568 | no_work_pending: | ||
569 | @ slow_restore_user_regs | ||
570 | restore_user_regs fast = 0, offset = 0 | ||
571 | ENDPROC(ret_to_user) | ||
572 | |||
573 | /* | ||
574 | * This is how we return from a fork. | ||
575 | */ | ||
576 | ENTRY(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 | ||
587 | ENDPROC(ret_from_fork) | ||
588 | |||
589 | /*============================================================================= | ||
590 | * SWI handler | ||
591 | *----------------------------------------------------------------------------- | ||
592 | */ | ||
593 | .align 5 | ||
594 | ENTRY(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 | ||
635 | 1: | ||
636 | add r1, sp, #S_OFF | ||
637 | 2: 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 | |||
674 | ENTRY(sys_execve) | ||
675 | add r3, sp, #S_OFF | ||
676 | b __sys_execve | ||
677 | ENDPROC(sys_execve) | ||
678 | |||
679 | ENTRY(sys_clone) | ||
680 | add ip, sp, #S_OFF | ||
681 | stw ip, [sp+], #4 | ||
682 | b __sys_clone | ||
683 | ENDPROC(sys_clone) | ||
684 | |||
685 | ENTRY(sys_rt_sigreturn) | ||
686 | add r0, sp, #S_OFF | ||
687 | mov why, #0 @ prevent syscall restart handling | ||
688 | b __sys_rt_sigreturn | ||
689 | ENDPROC(sys_rt_sigreturn) | ||
690 | |||
691 | ENTRY(sys_sigaltstack) | ||
692 | ldw r2, [sp+], #S_OFF + S_SP | ||
693 | b do_sigaltstack | ||
694 | ENDPROC(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 | |||
714 | vector_\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 | ||
739 | ENDPROC(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 | ||
821 | cr_alignment: | ||
822 | .space 4 | ||
823 | cr_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 | */ | ||
55 | void 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 | */ | ||
78 | void 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 | */ | ||
117 | static 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 | |||
126 | late_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 | |||
24 | static 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 | |||
31 | static 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 | |||
36 | static 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 | |||
44 | static int __init puv3_gpio_leds_init(void) | ||
45 | { | ||
46 | platform_device_register(&puv3_gpio_gpio_leds); | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | device_initcall(puv3_gpio_leds_init); | ||
51 | #endif | ||
52 | |||
53 | static int puv3_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
54 | { | ||
55 | return readl(GPIO_GPLR) & GPIO_GPIO(offset); | ||
56 | } | ||
57 | |||
58 | static 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 | |||
66 | static 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 | |||
76 | static 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 | |||
88 | static 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 | |||
98 | void __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 | ||
61 | ENTRY(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 | ||
81 | 101: 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 | ||
113 | 102: csub.a r0, r6 | ||
114 | add r1, r1, #1 << 22 | ||
115 | bua 103f | ||
116 | stw.w r1, [r0]+, #4 | ||
117 | b 102b | ||
118 | 103: | ||
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 | ||
164 | ENDPROC(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 | ||
180 | ENDPROC(__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) | ||
218 | 203: csub.a r6, r7 | ||
219 | bea 204f | ||
220 | stw.w fp, [r6]+,#4 | ||
221 | b 203b | ||
222 | 204: | ||
223 | andn r1, r0, #CR_A @ Clear 'A' bit | ||
224 | stm (r0, r1), [r8]+ @ Save control register values | ||
225 | b start_kernel | ||
226 | ENDPROC(__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 | ||
245 | 901: nop8 | ||
246 | b 901b | ||
247 | str_p1: .asciz "\nError: unrecognized processor variant (0x" | ||
248 | str_p2: .asciz ").\n" | ||
249 | .align | ||
250 | #endif | ||
251 | ENDPROC(__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 */ | ||
27 | pgd_t *resume_pg_dir; | ||
28 | |||
29 | struct 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 | */ | ||
36 | static 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 | */ | ||
51 | static 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 | */ | ||
73 | static 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 | |||
119 | static inline void resume_init_first_level_page_table(pgd_t *pg_dir) | ||
120 | { | ||
121 | } | ||
122 | |||
123 | int 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 | |||
145 | int 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 | |||
153 | void save_processor_state(void) | ||
154 | { | ||
155 | } | ||
156 | |||
157 | void 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 | @ | ||
28 | ENTRY(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 | ||
42 | 101: | ||
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 | ||
50 | 102: 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 | ||
59 | 109: | ||
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 | ||
91 | 1: .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 | |||
99 | ENTRY(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 | |||
117 | 1: .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 | |||
23 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
24 | static 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 | */ | ||
35 | union 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 | */ | ||
43 | struct task_struct init_task = INIT_TASK(init_task); | ||
44 | EXPORT_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 | */ | ||
39 | static int GPIO_IRQ_rising_edge; | ||
40 | static int GPIO_IRQ_falling_edge; | ||
41 | static int GPIO_IRQ_mask = 0; | ||
42 | |||
43 | #define GPIO_MASK(irq) (1 << (irq - IRQ_GPIO0)) | ||
44 | |||
45 | static 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 | */ | ||
78 | static void puv3_low_gpio_ack(struct irq_data *d) | ||
79 | { | ||
80 | writel((1 << d->irq), GPIO_GEDR); | ||
81 | } | ||
82 | |||
83 | static void puv3_low_gpio_mask(struct irq_data *d) | ||
84 | { | ||
85 | writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); | ||
86 | } | ||
87 | |||
88 | static void puv3_low_gpio_unmask(struct irq_data *d) | ||
89 | { | ||
90 | writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR); | ||
91 | } | ||
92 | |||
93 | static 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 | |||
102 | static 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 | */ | ||
116 | static void | ||
117 | puv3_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 | */ | ||
145 | static 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 | |||
152 | static 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 | |||
162 | static 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 | |||
172 | static 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 | |||
181 | static 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 | */ | ||
194 | static void puv3_mask_irq(struct irq_data *d) | ||
195 | { | ||
196 | writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR); | ||
197 | } | ||
198 | |||
199 | static 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 | */ | ||
207 | static 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 | |||
219 | static 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 | |||
227 | static struct resource irq_resource = { | ||
228 | .name = "irqs", | ||
229 | .start = io_v2p(PKUNITY_INTC_BASE), | ||
230 | .end = io_v2p(PKUNITY_INTC_BASE) + 0xFFFFF, | ||
231 | }; | ||
232 | |||
233 | static 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 | |||
240 | static 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 | |||
268 | static 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 | |||
284 | static struct sysdev_class puv3_irq_sysclass = { | ||
285 | .name = "pkunity-irq", | ||
286 | .suspend = puv3_irq_suspend, | ||
287 | .resume = puv3_irq_resume, | ||
288 | }; | ||
289 | |||
290 | static struct sys_device puv3_irq_device = { | ||
291 | .id = 0, | ||
292 | .cls = &puv3_irq_sysclass, | ||
293 | }; | ||
294 | |||
295 | static int __init puv3_irq_init_devicefs(void) | ||
296 | { | ||
297 | sysdev_class_register(&puv3_irq_sysclass); | ||
298 | return sysdev_register(&puv3_irq_device); | ||
299 | } | ||
300 | |||
301 | device_initcall(puv3_irq_init_devicefs); | ||
302 | |||
303 | void __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 | |||
358 | int 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'); | ||
392 | unlock: | ||
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 | */ | ||
405 | asmlinkage 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 | |||
27 | EXPORT_SYMBOL(__uc32_find_next_zero_bit); | ||
28 | EXPORT_SYMBOL(__uc32_find_next_bit); | ||
29 | |||
30 | EXPORT_SYMBOL(__backtrace); | ||
31 | |||
32 | /* platform dependent support */ | ||
33 | EXPORT_SYMBOL(__udelay); | ||
34 | EXPORT_SYMBOL(__const_udelay); | ||
35 | |||
36 | /* networking */ | ||
37 | EXPORT_SYMBOL(csum_partial); | ||
38 | EXPORT_SYMBOL(csum_partial_copy_from_user); | ||
39 | EXPORT_SYMBOL(csum_partial_copy_nocheck); | ||
40 | EXPORT_SYMBOL(__csum_ipv6_magic); | ||
41 | |||
42 | /* io */ | ||
43 | #ifndef __raw_readsb | ||
44 | EXPORT_SYMBOL(__raw_readsb); | ||
45 | #endif | ||
46 | #ifndef __raw_readsw | ||
47 | EXPORT_SYMBOL(__raw_readsw); | ||
48 | #endif | ||
49 | #ifndef __raw_readsl | ||
50 | EXPORT_SYMBOL(__raw_readsl); | ||
51 | #endif | ||
52 | #ifndef __raw_writesb | ||
53 | EXPORT_SYMBOL(__raw_writesb); | ||
54 | #endif | ||
55 | #ifndef __raw_writesw | ||
56 | EXPORT_SYMBOL(__raw_writesw); | ||
57 | #endif | ||
58 | #ifndef __raw_writesl | ||
59 | EXPORT_SYMBOL(__raw_writesl); | ||
60 | #endif | ||
61 | |||
62 | /* string / mem functions */ | ||
63 | EXPORT_SYMBOL(strchr); | ||
64 | EXPORT_SYMBOL(strrchr); | ||
65 | EXPORT_SYMBOL(memset); | ||
66 | EXPORT_SYMBOL(memcpy); | ||
67 | EXPORT_SYMBOL(memmove); | ||
68 | EXPORT_SYMBOL(memchr); | ||
69 | |||
70 | /* user mem (segment) */ | ||
71 | EXPORT_SYMBOL(__strnlen_user); | ||
72 | EXPORT_SYMBOL(__strncpy_from_user); | ||
73 | |||
74 | EXPORT_SYMBOL(copy_page); | ||
75 | |||
76 | EXPORT_SYMBOL(__copy_from_user); | ||
77 | EXPORT_SYMBOL(__copy_to_user); | ||
78 | EXPORT_SYMBOL(__clear_user); | ||
79 | |||
80 | EXPORT_SYMBOL(__get_user_1); | ||
81 | EXPORT_SYMBOL(__get_user_2); | ||
82 | EXPORT_SYMBOL(__get_user_4); | ||
83 | |||
84 | EXPORT_SYMBOL(__put_user_1); | ||
85 | EXPORT_SYMBOL(__put_user_2); | ||
86 | EXPORT_SYMBOL(__put_user_4); | ||
87 | EXPORT_SYMBOL(__put_user_8); | ||
88 | |||
89 | EXPORT_SYMBOL(__ashldi3); | ||
90 | EXPORT_SYMBOL(__ashrdi3); | ||
91 | EXPORT_SYMBOL(__divsi3); | ||
92 | EXPORT_SYMBOL(__lshrdi3); | ||
93 | EXPORT_SYMBOL(__modsi3); | ||
94 | EXPORT_SYMBOL(__muldi3); | ||
95 | EXPORT_SYMBOL(__ucmpdi2); | ||
96 | EXPORT_SYMBOL(__udivsi3); | ||
97 | EXPORT_SYMBOL(__umodsi3); | ||
98 | EXPORT_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 | */ | ||
6 | extern void __ashldi3(void); | ||
7 | extern void __ashrdi3(void); | ||
8 | extern void __divsi3(void); | ||
9 | extern void __lshrdi3(void); | ||
10 | extern void __modsi3(void); | ||
11 | extern void __muldi3(void); | ||
12 | extern void __ucmpdi2(void); | ||
13 | extern void __udivsi3(void); | ||
14 | extern void __umodsi3(void); | ||
15 | extern 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 | |||
25 | void *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 | |||
40 | void module_free(struct module *module, void *region) | ||
41 | { | ||
42 | vfree(region); | ||
43 | } | ||
44 | |||
45 | int module_frob_arch_sections(Elf_Ehdr *hdr, | ||
46 | Elf_Shdr *sechdrs, | ||
47 | char *secstrings, | ||
48 | struct module *mod) | ||
49 | { | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | int | ||
54 | apply_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 | |||
132 | int | ||
133 | apply_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 | |||
142 | int | ||
143 | module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, | ||
144 | struct module *module) | ||
145 | { | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | void | ||
150 | module_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 | |||
23 | static int debug_pci; | ||
24 | static int use_firmware; | ||
25 | |||
26 | #define CONFIG_CMD(bus, devfn, where) \ | ||
27 | (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) | ||
28 | |||
29 | static int | ||
30 | puv3_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 | |||
48 | static int | ||
49 | puv3_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 | |||
69 | struct pci_ops pci_puv3_ops = { | ||
70 | .read = puv3_read_config, | ||
71 | .write = puv3_write_config, | ||
72 | }; | ||
73 | |||
74 | void 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 | |||
105 | static 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 | */ | ||
141 | void __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 | |||
158 | void __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 | */ | ||
170 | static 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 | */ | ||
179 | void __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 | ||
263 | EXPORT_SYMBOL(pcibios_fixup_bus); | ||
264 | #endif | ||
265 | |||
266 | static 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 | } | ||
298 | subsys_initcall(pci_common_init); | ||
299 | |||
300 | char * __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 | */ | ||
327 | resource_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 | */ | ||
344 | int 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 | |||
383 | int 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 | |||
25 | struct puv3_cpu_pm_fns *puv3_cpu_pm_fns; | ||
26 | static unsigned long *sleep_save; | ||
27 | |||
28 | int 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 | } | ||
67 | EXPORT_SYMBOL_GPL(puv3_pm_enter); | ||
68 | |||
69 | unsigned long sleep_phys_sp(void *sp) | ||
70 | { | ||
71 | return virt_to_phys(sp); | ||
72 | } | ||
73 | |||
74 | static 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 | |||
82 | static 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 | |||
92 | static 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 | |||
98 | static 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 | |||
105 | static 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 | |||
123 | device_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 | |||
42 | static 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 | */ | ||
54 | void 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 | |||
73 | static char reboot_mode = 'h'; | ||
74 | |||
75 | int __init reboot_setup(char *str) | ||
76 | { | ||
77 | reboot_mode = str[0]; | ||
78 | return 1; | ||
79 | } | ||
80 | |||
81 | __setup("reboot=", reboot_setup); | ||
82 | |||
83 | void machine_halt(void) | ||
84 | { | ||
85 | gpio_set_value(GPO_SOFT_OFF, 0); | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * Function pointers to optional machine specific functions | ||
90 | */ | ||
91 | void (*pm_power_off)(void) = NULL; | ||
92 | |||
93 | void machine_power_off(void) | ||
94 | { | ||
95 | if (pm_power_off) | ||
96 | pm_power_off(); | ||
97 | machine_halt(); | ||
98 | } | ||
99 | |||
100 | void 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 | |||
161 | void __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 | |||
227 | void 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 | */ | ||
239 | void exit_thread(void) | ||
240 | { | ||
241 | } | ||
242 | |||
243 | void 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 | |||
255 | void release_thread(struct task_struct *dead_task) | ||
256 | { | ||
257 | } | ||
258 | |||
259 | asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); | ||
260 | |||
261 | int | ||
262 | copy_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 | */ | ||
285 | int 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 | */ | ||
294 | int 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 | } | ||
305 | EXPORT_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 | */ | ||
312 | asm(".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 | */ | ||
326 | pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
327 | { | ||
328 | struct pt_regs regs; | ||
329 | |||
330 | memset(®s, 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, ®s, 0, NULL, NULL); | ||
340 | } | ||
341 | EXPORT_SYMBOL(kernel_thread); | ||
342 | |||
343 | unsigned 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 | |||
364 | unsigned 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 | |||
376 | int 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 | |||
386 | const 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 | */ | ||
25 | static 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 | */ | ||
36 | static inline int | ||
37 | put_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 | */ | ||
56 | void ptrace_disable(struct task_struct *child) | ||
57 | { | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * We actually access the pt_regs stored on the kernel stack. | ||
62 | */ | ||
63 | static 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 | */ | ||
78 | static 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 | |||
87 | long 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 | |||
115 | asmlinkage 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 | */ | ||
37 | unsigned 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 | |||
50 | static 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 | |||
65 | static 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 | |||
76 | static 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 | |||
83 | static 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 | |||
96 | static 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 | |||
109 | static 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 | |||
127 | static 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 | |||
135 | static 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 | |||
148 | static 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 | |||
161 | static 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 | */ | ||
184 | enum { | ||
185 | SLEEP_SAVE_PM_PLLDDRCFG, | ||
186 | SLEEP_SAVE_COUNT | ||
187 | }; | ||
188 | |||
189 | |||
190 | static void puv3_cpu_pm_save(unsigned long *sleep_save) | ||
191 | { | ||
192 | /* SAVE(PM_PLLDDRCFG); */ | ||
193 | } | ||
194 | |||
195 | static void puv3_cpu_pm_restore(unsigned long *sleep_save) | ||
196 | { | ||
197 | /* RESTORE(PM_PLLDDRCFG); */ | ||
198 | } | ||
199 | |||
200 | static 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 | |||
207 | static 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 | |||
224 | static int puv3_cpu_pm_valid(suspend_state_t state) | ||
225 | { | ||
226 | return state == PM_SUSPEND_MEM; | ||
227 | } | ||
228 | |||
229 | static void puv3_cpu_pm_finish(void) | ||
230 | { | ||
231 | /* ensure not to come back here if it wasn't intended */ | ||
232 | /* PSPR = 0; */ | ||
233 | } | ||
234 | |||
235 | static 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 | |||
245 | static void __init puv3_init_pm(void) | ||
246 | { | ||
247 | puv3_cpu_pm_fns = &puv3_cpu_pm_fnss; | ||
248 | } | ||
249 | #else | ||
250 | static inline void puv3_init_pm(void) {} | ||
251 | #endif | ||
252 | |||
253 | void 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 | |||
261 | void __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 | |||
30 | static struct physmap_flash_data physmap_flash_data = { | ||
31 | .width = 1, | ||
32 | }; | ||
33 | |||
34 | static struct resource physmap_flash_resource = { | ||
35 | .start = 0xFFF80000, | ||
36 | .end = 0xFFFFFFFF, | ||
37 | .flags = IORESOURCE_MEM, | ||
38 | }; | ||
39 | |||
40 | static 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 | |||
53 | static 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 | |||
60 | static 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 | |||
79 | static struct gpio_keys_platform_data nb0916_gpio_button_data = { | ||
80 | .buttons = nb0916_gpio_keys, | ||
81 | .nbuttons = ARRAY_SIZE(nb0916_gpio_keys), | ||
82 | }; | ||
83 | |||
84 | static 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 | |||
94 | static 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 | |||
101 | static 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 | |||
107 | int __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 | |||
145 | subsys_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 | |||
26 | struct 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 | */ | ||
42 | int 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 | } | ||
79 | EXPORT_SYMBOL(pwm_config); | ||
80 | |||
81 | int 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 | } | ||
92 | EXPORT_SYMBOL(pwm_enable); | ||
93 | |||
94 | void pwm_disable(struct pwm_device *pwm) | ||
95 | { | ||
96 | if (pwm->clk_enabled) { | ||
97 | clk_disable(pwm->clk); | ||
98 | pwm->clk_enabled = 0; | ||
99 | } | ||
100 | } | ||
101 | EXPORT_SYMBOL(pwm_disable); | ||
102 | |||
103 | static DEFINE_MUTEX(pwm_lock); | ||
104 | static LIST_HEAD(pwm_list); | ||
105 | |||
106 | struct 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 | } | ||
132 | EXPORT_SYMBOL(pwm_request); | ||
133 | |||
134 | void 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 | } | ||
146 | EXPORT_SYMBOL(pwm_free); | ||
147 | |||
148 | static 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 | |||
155 | static 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 | |||
197 | err_free_clk: | ||
198 | clk_put(pwm->clk); | ||
199 | err_free: | ||
200 | kfree(pwm); | ||
201 | return ERR_PTR(ret); | ||
202 | } | ||
203 | |||
204 | static 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 | |||
214 | static 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 | |||
235 | static 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 | |||
243 | static 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 | } | ||
255 | arch_initcall(pwm_init); | ||
256 | |||
257 | static void __exit pwm_exit(void) | ||
258 | { | ||
259 | platform_driver_unregister(&puv3_pwm_driver); | ||
260 | } | ||
261 | module_exit(pwm_exit); | ||
262 | |||
263 | MODULE_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 | |||
31 | static struct resource *puv3_rtc_mem; | ||
32 | |||
33 | static int puv3_rtc_alarmno = IRQ_RTCAlarm; | ||
34 | static int puv3_rtc_tickno = IRQ_RTC; | ||
35 | |||
36 | static DEFINE_SPINLOCK(puv3_rtc_pie_lock); | ||
37 | |||
38 | /* IRQ Handlers */ | ||
39 | |||
40 | static 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 | |||
49 | static 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 */ | ||
59 | static 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 | |||
73 | static 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 | |||
91 | static int puv3_rtc_setfreq(struct device *dev, int freq) | ||
92 | { | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | /* Time read/write */ | ||
97 | |||
98 | static 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 | |||
109 | static 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 | |||
123 | static 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 | |||
139 | static 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 | |||
162 | static 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 | |||
169 | static 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 | |||
198 | static 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 | |||
210 | static 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 | |||
222 | static 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 | |||
236 | static 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 | |||
252 | static 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 | |||
329 | static int ticnt_save; | ||
330 | |||
331 | static 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 | |||
339 | static 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 | |||
350 | static 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 | |||
361 | static char __initdata banner[] = "PKUnity-v3 RTC, (c) 2009 PKUnity Co.\n"; | ||
362 | |||
363 | static int __init puv3_rtc_init(void) | ||
364 | { | ||
365 | printk(banner); | ||
366 | return platform_driver_register(&puv3_rtcdrv); | ||
367 | } | ||
368 | |||
369 | static void __exit puv3_rtc_exit(void) | ||
370 | { | ||
371 | platform_driver_unregister(&puv3_rtcdrv); | ||
372 | } | ||
373 | |||
374 | module_init(puv3_rtc_init); | ||
375 | module_exit(puv3_rtc_exit); | ||
376 | |||
377 | MODULE_DESCRIPTION("RTC Driver for the PKUnity v3 chip"); | ||
378 | MODULE_AUTHOR("Hu Dongliang"); | ||
379 | MODULE_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 | |||
47 | struct stack { | ||
48 | u32 irq[3]; | ||
49 | u32 abt[3]; | ||
50 | u32 und[3]; | ||
51 | } ____cacheline_aligned; | ||
52 | |||
53 | static struct stack stacks[NR_CPUS]; | ||
54 | |||
55 | char elf_platform[ELF_PLATFORM_SIZE]; | ||
56 | EXPORT_SYMBOL(elf_platform); | ||
57 | |||
58 | static char __initdata cmd_line[COMMAND_LINE_SIZE]; | ||
59 | |||
60 | static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; | ||
61 | |||
62 | /* | ||
63 | * Standard memory resources | ||
64 | */ | ||
65 | static 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 | */ | ||
94 | static 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 | */ | ||
108 | void 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 | |||
136 | static 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 | */ | ||
170 | static 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 | } | ||
195 | early_param("mem", early_mem); | ||
196 | |||
197 | static void __init | ||
198 | request_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 | |||
233 | static void (*init_machine)(void) __initdata; | ||
234 | |||
235 | static 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 | } | ||
242 | arch_initcall(customize_machine); | ||
243 | |||
244 | void __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 | |||
286 | static struct cpu cpuinfo_unicore; | ||
287 | |||
288 | static 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 | } | ||
297 | subsys_initcall(topology_init); | ||
298 | |||
299 | #ifdef CONFIG_HAVE_PROC_CPU | ||
300 | static 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 | } | ||
309 | fs_initcall(proc_cpu_init); | ||
310 | #endif | ||
311 | |||
312 | static 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 | |||
340 | static void *c_start(struct seq_file *m, loff_t *pos) | ||
341 | { | ||
342 | return *pos < 1 ? (void *)1 : NULL; | ||
343 | } | ||
344 | |||
345 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||
346 | { | ||
347 | ++*pos; | ||
348 | return NULL; | ||
349 | } | ||
350 | |||
351 | static void c_stop(struct seq_file *m, void *v) | ||
352 | { | ||
353 | } | ||
354 | |||
355 | const 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 | |||
15 | extern void paging_init(void); | ||
16 | extern void puv3_core_init(void); | ||
17 | |||
18 | extern void puv3_ps2_init(void); | ||
19 | extern void pci_puv3_preinit(void); | ||
20 | extern void __init puv3_init_gpio(void); | ||
21 | |||
22 | extern void setup_mm_for_reboot(char mode); | ||
23 | |||
24 | extern char __stubs_start[], __stubs_end[]; | ||
25 | extern char __vectors_start[], __vectors_end[]; | ||
26 | |||
27 | extern void kernel_thread_helper(void); | ||
28 | |||
29 | extern 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 | |||
36 | const unsigned long sigreturn_codes[3] = { | ||
37 | SWI_SYS_SIGRETURN, SWI_SYS_RT_SIGRETURN, | ||
38 | }; | ||
39 | |||
40 | const 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 | */ | ||
48 | struct sigframe { | ||
49 | struct ucontext uc; | ||
50 | unsigned long retcode[2]; | ||
51 | }; | ||
52 | |||
53 | struct rt_sigframe { | ||
54 | struct siginfo info; | ||
55 | struct sigframe sig; | ||
56 | }; | ||
57 | |||
58 | static 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(¤t->sighand->siglock); | ||
67 | current->blocked = set; | ||
68 | recalc_sigpending(); | ||
69 | spin_unlock_irq(¤t->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 | |||
111 | asmlinkage 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 | |||
140 | badframe: | ||
141 | force_sig(SIGSEGV, current); | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static 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 | |||
197 | static 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 | |||
223 | static 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 | |||
250 | static 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 | |||
271 | static 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 | |||
309 | static 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 | */ | ||
318 | static 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 | */ | ||
395 | static 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 = ¤t->saved_sigmask; | ||
419 | else | ||
420 | oldset = ¤t->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, ¤t->saved_sigmask, NULL); | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | |||
469 | asmlinkage 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 | */ | ||
487 | void __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 | |||
20 | pkunity_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 | |||
35 | pkunity_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 | |||
52 | ENTRY(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 | ||
118 | pkunity_cpu_do_suspend: | ||
119 | b 101f | ||
120 | @ put DDR2 into self-refresh | ||
121 | 100: stw r5, [r0+], #0x24 | ||
122 | @ change PLL | ||
123 | stw r6, [r1] | ||
124 | b 1f | ||
125 | |||
126 | .ltorg | ||
127 | .align 5 | ||
128 | 101: b 102f | ||
129 | @ wait for PLL changing complete | ||
130 | 1: ldw r6, [r1+], #0x44 | ||
131 | csub.a r6, #0x1 | ||
132 | bne 1b | ||
133 | b 2f | ||
134 | |||
135 | .ltorg | ||
136 | .align 5 | ||
137 | 102: b 100b | ||
138 | @ close PLL | ||
139 | 2: stw r7, [r1+], #0x4 | ||
140 | @ enter sleep mode | ||
141 | stw r8, [r1] | ||
142 | 3: 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 | ||
161 | ENTRY(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 | |||
185 | sleep_save_sp: | ||
186 | .word 0 @ preserve stack phys ptr here | ||
187 | |||
188 | .text | ||
189 | resume_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 | */ | ||
35 | int 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 | |||
57 | void 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 | } | ||
70 | EXPORT_SYMBOL(walk_stackframe); | ||
71 | |||
72 | #ifdef CONFIG_STACKTRACE | ||
73 | struct stack_trace_data { | ||
74 | struct stack_trace *trace; | ||
75 | unsigned int no_sched_functions; | ||
76 | unsigned int skip; | ||
77 | }; | ||
78 | |||
79 | static 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 | |||
97 | void 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 | |||
126 | void save_stack_trace(struct stack_trace *trace) | ||
127 | { | ||
128 | save_stack_trace_tsk(current, trace); | ||
129 | } | ||
130 | EXPORT_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 | */ | ||
34 | asmlinkage 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 | */ | ||
48 | asmlinkage 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); | ||
62 | out: | ||
63 | return error; | ||
64 | } | ||
65 | |||
66 | int 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(®s, 0, sizeof(struct pt_regs)); | ||
74 | ret = do_execve(filename, | ||
75 | (const char __user *const __user *)argv, | ||
76 | (const char __user *const __user *)envp, ®s); | ||
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" (®s), | ||
101 | "Ir" (sizeof(regs)) | ||
102 | : "r0", "r1", "r2", "r3", "ip", "lr", "memory"); | ||
103 | |||
104 | out: | ||
105 | return ret; | ||
106 | } | ||
107 | EXPORT_SYMBOL(kernel_execve); | ||
108 | |||
109 | /* Note: used by the compat code even in 64-bit Linux. */ | ||
110 | SYSCALL_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> */ | ||
123 | void *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 | |||
24 | static 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 | |||
36 | static int | ||
37 | puv3_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 | |||
49 | static void | ||
50 | puv3_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 | |||
66 | static 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 | |||
74 | static cycle_t puv3_read_oscr(struct clocksource *cs) | ||
75 | { | ||
76 | return readl(OST_OSCR); | ||
77 | } | ||
78 | |||
79 | static 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 | |||
87 | static 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 | |||
94 | void __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 | ||
114 | unsigned long osmr[4], oier; | ||
115 | |||
116 | void 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 | |||
125 | void 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 | ||
140 | void puv3_timer_suspend(void) { }; | ||
141 | void 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 | |||
35 | static void dump_mem(const char *, const char *, unsigned long, unsigned long); | ||
36 | |||
37 | void 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 | */ | ||
54 | static 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 | */ | ||
66 | static 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 | |||
106 | static 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 | |||
140 | static 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 | |||
175 | void dump_stack(void) | ||
176 | { | ||
177 | dump_backtrace(NULL, NULL); | ||
178 | } | ||
179 | EXPORT_SYMBOL(dump_stack); | ||
180 | |||
181 | void show_stack(struct task_struct *tsk, unsigned long *sp) | ||
182 | { | ||
183 | dump_backtrace(NULL, tsk); | ||
184 | barrier(); | ||
185 | } | ||
186 | |||
187 | static 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 | |||
219 | DEFINE_SPINLOCK(die_lock); | ||
220 | |||
221 | /* | ||
222 | * This function is protected against re-entrancy. | ||
223 | */ | ||
224 | void 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 | |||
249 | void 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 | */ | ||
267 | asmlinkage 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 | |||
278 | void __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 | |||
283 | void __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 | |||
288 | void __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 | |||
293 | asmlinkage void __div0(void) | ||
294 | { | ||
295 | printk(KERN_DEFAULT "Division by zero in kernel.\n"); | ||
296 | dump_stack(); | ||
297 | } | ||
298 | EXPORT_SYMBOL(__div0); | ||
299 | |||
300 | void abort(void) | ||
301 | { | ||
302 | BUG(); | ||
303 | |||
304 | /* if that doesn't kill us, halt */ | ||
305 | panic("Oops failed to kill thread"); | ||
306 | } | ||
307 | EXPORT_SYMBOL(abort); | ||
308 | |||
309 | void __init trap_init(void) | ||
310 | { | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | void __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 | |||
18 | OUTPUT_ARCH(unicore32) | ||
19 | ENTRY(stext) | ||
20 | |||
21 | jiffies = jiffies_64; | ||
22 | |||
23 | SECTIONS | ||
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 | } | ||