Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
#
# Makefile for the linux kernel.
#
# Object file lists.
obj-y := dma.o elf.o entry.o process.o ptrace.o
obj-y += setup.o signal.o sys.o stacktrace.o traps.o
obj-$(CONFIG_MODULES) += ksyms.o module.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_UNICORE_FPU_F64) += fpu-ucf64.o
# obj-y for architecture PKUnity v3
obj-$(CONFIG_ARCH_PUV3) += clock.o irq.o time.o
obj-$(CONFIG_PUV3_GPIO) += gpio.o
obj-$(CONFIG_PUV3_PM) += pm.o sleep.o
obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o
obj-$(CONFIG_PCI) += pci.o
# obj-y for specific machines
obj-$(CONFIG_ARCH_PUV3) += puv3-core.o
obj-$(CONFIG_PUV3_NB0916) += puv3-nb0916.o
head-y := head.o
obj-$(CONFIG_DEBUG_LL) += debug.o
extra-y := $(head-y) vmlinux.lds

View File

@@ -0,0 +1,112 @@
/*
* linux/arch/unicore32/kernel/asm-offsets.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* Generate definitions needed by assembly language modules.
* This code generates raw asm output which is post-processed to extract
* and format the required data.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/kbuild.h>
#include <linux/suspend.h>
#include <linux/thread_info.h>
#include <asm/memory.h>
#include <asm/suspend.h>
/*
* GCC 3.0, 3.1: general bad code generation.
* GCC 3.2.0: incorrect function argument offset calculation.
* GCC 3.2.x: miscompiles NEW_AUX_ENT in fs/binfmt_elf.c
* (http://gcc.gnu.org/PR8896) and incorrect structure
* initialisation in fs/jffs2/erase.c
*/
#if (__GNUC__ < 4)
#error Your compiler should upgrade to uc4
#error Known good compilers: 4.2.2
#endif
int main(void)
{
DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
BLANK();
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
DEFINE(TI_TASK, offsetof(struct thread_info, task));
DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));
DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp));
#ifdef CONFIG_UNICORE_FPU_F64
DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
#endif
BLANK();
DEFINE(S_R0, offsetof(struct pt_regs, UCreg_00));
DEFINE(S_R1, offsetof(struct pt_regs, UCreg_01));
DEFINE(S_R2, offsetof(struct pt_regs, UCreg_02));
DEFINE(S_R3, offsetof(struct pt_regs, UCreg_03));
DEFINE(S_R4, offsetof(struct pt_regs, UCreg_04));
DEFINE(S_R5, offsetof(struct pt_regs, UCreg_05));
DEFINE(S_R6, offsetof(struct pt_regs, UCreg_06));
DEFINE(S_R7, offsetof(struct pt_regs, UCreg_07));
DEFINE(S_R8, offsetof(struct pt_regs, UCreg_08));
DEFINE(S_R9, offsetof(struct pt_regs, UCreg_09));
DEFINE(S_R10, offsetof(struct pt_regs, UCreg_10));
DEFINE(S_R11, offsetof(struct pt_regs, UCreg_11));
DEFINE(S_R12, offsetof(struct pt_regs, UCreg_12));
DEFINE(S_R13, offsetof(struct pt_regs, UCreg_13));
DEFINE(S_R14, offsetof(struct pt_regs, UCreg_14));
DEFINE(S_R15, offsetof(struct pt_regs, UCreg_15));
DEFINE(S_R16, offsetof(struct pt_regs, UCreg_16));
DEFINE(S_R17, offsetof(struct pt_regs, UCreg_17));
DEFINE(S_R18, offsetof(struct pt_regs, UCreg_18));
DEFINE(S_R19, offsetof(struct pt_regs, UCreg_19));
DEFINE(S_R20, offsetof(struct pt_regs, UCreg_20));
DEFINE(S_R21, offsetof(struct pt_regs, UCreg_21));
DEFINE(S_R22, offsetof(struct pt_regs, UCreg_22));
DEFINE(S_R23, offsetof(struct pt_regs, UCreg_23));
DEFINE(S_R24, offsetof(struct pt_regs, UCreg_24));
DEFINE(S_R25, offsetof(struct pt_regs, UCreg_25));
DEFINE(S_R26, offsetof(struct pt_regs, UCreg_26));
DEFINE(S_FP, offsetof(struct pt_regs, UCreg_fp));
DEFINE(S_IP, offsetof(struct pt_regs, UCreg_ip));
DEFINE(S_SP, offsetof(struct pt_regs, UCreg_sp));
DEFINE(S_LR, offsetof(struct pt_regs, UCreg_lr));
DEFINE(S_PC, offsetof(struct pt_regs, UCreg_pc));
DEFINE(S_PSR, offsetof(struct pt_regs, UCreg_asr));
DEFINE(S_OLD_R0, offsetof(struct pt_regs, UCreg_ORIG_00));
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
BLANK();
DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
BLANK();
DEFINE(VM_EXEC, VM_EXEC);
BLANK();
DEFINE(PAGE_SZ, PAGE_SIZE);
BLANK();
DEFINE(SYS_ERROR0, 0x9f0000);
BLANK();
DEFINE(PBE_ADDRESS, offsetof(struct pbe, address));
DEFINE(PBE_ORIN_ADDRESS, offsetof(struct pbe, orig_address));
DEFINE(PBE_NEXT, offsetof(struct pbe, next));
DEFINE(SWSUSP_CPU, offsetof(struct swsusp_arch_regs, \
cpu_context));
#ifdef CONFIG_UNICORE_FPU_F64
DEFINE(SWSUSP_FPSTATE, offsetof(struct swsusp_arch_regs, \
fpstate));
#endif
BLANK();
DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE);
DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE);
return 0;
}

View File

@@ -0,0 +1,390 @@
/*
* linux/arch/unicore32/kernel/clock.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <mach/hardware.h>
/*
* Very simple clock implementation
*/
struct clk {
struct list_head node;
unsigned long rate;
const char *name;
};
static struct clk clk_ost_clk = {
.name = "OST_CLK",
.rate = CLOCK_TICK_RATE,
};
static struct clk clk_mclk_clk = {
.name = "MAIN_CLK",
};
static struct clk clk_bclk32_clk = {
.name = "BUS32_CLK",
};
static struct clk clk_ddr_clk = {
.name = "DDR_CLK",
};
static struct clk clk_vga_clk = {
.name = "VGA_CLK",
};
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
struct clk *clk_get(struct device *dev, const char *id)
{
struct clk *p, *clk = ERR_PTR(-ENOENT);
mutex_lock(&clocks_mutex);
list_for_each_entry(p, &clocks, node) {
if (strcmp(id, p->name) == 0) {
clk = p;
break;
}
}
mutex_unlock(&clocks_mutex);
return clk;
}
EXPORT_SYMBOL(clk_get);
void clk_put(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_put);
int clk_enable(struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate(struct clk *clk)
{
return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
struct {
unsigned long rate;
unsigned long cfg;
unsigned long div;
} vga_clk_table[] = {
{.rate = 25175000, .cfg = 0x00002001, .div = 0x9},
{.rate = 31500000, .cfg = 0x00002001, .div = 0x7},
{.rate = 40000000, .cfg = 0x00003801, .div = 0x9},
{.rate = 49500000, .cfg = 0x00003801, .div = 0x7},
{.rate = 65000000, .cfg = 0x00002c01, .div = 0x4},
{.rate = 78750000, .cfg = 0x00002400, .div = 0x7},
{.rate = 108000000, .cfg = 0x00002c01, .div = 0x2},
{.rate = 106500000, .cfg = 0x00003c01, .div = 0x3},
{.rate = 50650000, .cfg = 0x00106400, .div = 0x9},
{.rate = 61500000, .cfg = 0x00106400, .div = 0xa},
{.rate = 85500000, .cfg = 0x00002800, .div = 0x6},
};
struct {
unsigned long mrate;
unsigned long prate;
} mclk_clk_table[] = {
{.mrate = 500000000, .prate = 0x00109801},
{.mrate = 525000000, .prate = 0x00104C00},
{.mrate = 550000000, .prate = 0x00105000},
{.mrate = 575000000, .prate = 0x00105400},
{.mrate = 600000000, .prate = 0x00105800},
{.mrate = 625000000, .prate = 0x00105C00},
{.mrate = 650000000, .prate = 0x00106000},
{.mrate = 675000000, .prate = 0x00106400},
{.mrate = 700000000, .prate = 0x00106800},
{.mrate = 725000000, .prate = 0x00106C00},
{.mrate = 750000000, .prate = 0x00107000},
{.mrate = 775000000, .prate = 0x00107400},
{.mrate = 800000000, .prate = 0x00107800},
};
int clk_set_rate(struct clk *clk, unsigned long rate)
{
if (clk == &clk_vga_clk) {
unsigned long pll_vgacfg, pll_vgadiv;
int ret, i;
/* lookup vga_clk_table */
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(vga_clk_table); i++) {
if (rate == vga_clk_table[i].rate) {
pll_vgacfg = vga_clk_table[i].cfg;
pll_vgadiv = vga_clk_table[i].div;
ret = 0;
break;
}
}
if (ret)
return ret;
if (readl(PM_PLLVGACFG) == pll_vgacfg)
return 0;
/* set pll vga cfg reg. */
writel(pll_vgacfg, PM_PLLVGACFG);
writel(PM_PMCR_CFBVGA, PM_PMCR);
while ((readl(PM_PLLDFCDONE) & PM_PLLDFCDONE_VGADFC)
!= PM_PLLDFCDONE_VGADFC)
udelay(100); /* about 1ms */
/* set div cfg reg. */
writel(readl(PM_PCGR) | PM_PCGR_VGACLK, PM_PCGR);
writel((readl(PM_DIVCFG) & ~PM_DIVCFG_VGACLK_MASK)
| PM_DIVCFG_VGACLK(pll_vgadiv), PM_DIVCFG);
writel(readl(PM_SWRESET) | PM_SWRESET_VGADIV, PM_SWRESET);
while ((readl(PM_SWRESET) & PM_SWRESET_VGADIV)
== PM_SWRESET_VGADIV)
udelay(100); /* 65536 bclk32, about 320us */
writel(readl(PM_PCGR) & ~PM_PCGR_VGACLK, PM_PCGR);
}
#ifdef CONFIG_CPU_FREQ
if (clk == &clk_mclk_clk) {
u32 pll_rate, divstatus = PM_DIVSTATUS;
int ret, i;
/* lookup mclk_clk_table */
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(mclk_clk_table); i++) {
if (rate == mclk_clk_table[i].mrate) {
pll_rate = mclk_clk_table[i].prate;
clk_mclk_clk.rate = mclk_clk_table[i].mrate;
ret = 0;
break;
}
}
if (ret)
return ret;
if (clk_mclk_clk.rate)
clk_bclk32_clk.rate = clk_mclk_clk.rate
/ (((divstatus & 0x0000f000) >> 12) + 1);
/* set pll sys cfg reg. */
PM_PLLSYSCFG = pll_rate;
PM_PMCR = PM_PMCR_CFBSYS;
while ((PM_PLLDFCDONE & PM_PLLDFCDONE_SYSDFC)
!= PM_PLLDFCDONE_SYSDFC)
udelay(100);
/* about 1ms */
}
#endif
return 0;
}
EXPORT_SYMBOL(clk_set_rate);
int clk_register(struct clk *clk)
{
mutex_lock(&clocks_mutex);
list_add(&clk->node, &clocks);
mutex_unlock(&clocks_mutex);
printk(KERN_DEFAULT "PKUnity PM: %s %lu.%02luM\n", clk->name,
(clk->rate)/1000000, (clk->rate)/10000 % 100);
return 0;
}
EXPORT_SYMBOL(clk_register);
void clk_unregister(struct clk *clk)
{
mutex_lock(&clocks_mutex);
list_del(&clk->node);
mutex_unlock(&clocks_mutex);
}
EXPORT_SYMBOL(clk_unregister);
struct {
unsigned long prate;
unsigned long rate;
} pllrate_table[] = {
{.prate = 0x00002001, .rate = 250000000},
{.prate = 0x00104801, .rate = 250000000},
{.prate = 0x00104C01, .rate = 262500000},
{.prate = 0x00002401, .rate = 275000000},
{.prate = 0x00105001, .rate = 275000000},
{.prate = 0x00105401, .rate = 287500000},
{.prate = 0x00002801, .rate = 300000000},
{.prate = 0x00105801, .rate = 300000000},
{.prate = 0x00105C01, .rate = 312500000},
{.prate = 0x00002C01, .rate = 325000000},
{.prate = 0x00106001, .rate = 325000000},
{.prate = 0x00106401, .rate = 337500000},
{.prate = 0x00003001, .rate = 350000000},
{.prate = 0x00106801, .rate = 350000000},
{.prate = 0x00106C01, .rate = 362500000},
{.prate = 0x00003401, .rate = 375000000},
{.prate = 0x00107001, .rate = 375000000},
{.prate = 0x00107401, .rate = 387500000},
{.prate = 0x00003801, .rate = 400000000},
{.prate = 0x00107801, .rate = 400000000},
{.prate = 0x00107C01, .rate = 412500000},
{.prate = 0x00003C01, .rate = 425000000},
{.prate = 0x00108001, .rate = 425000000},
{.prate = 0x00108401, .rate = 437500000},
{.prate = 0x00004001, .rate = 450000000},
{.prate = 0x00108801, .rate = 450000000},
{.prate = 0x00108C01, .rate = 462500000},
{.prate = 0x00004401, .rate = 475000000},
{.prate = 0x00109001, .rate = 475000000},
{.prate = 0x00109401, .rate = 487500000},
{.prate = 0x00004801, .rate = 500000000},
{.prate = 0x00109801, .rate = 500000000},
{.prate = 0x00104C00, .rate = 525000000},
{.prate = 0x00002400, .rate = 550000000},
{.prate = 0x00105000, .rate = 550000000},
{.prate = 0x00105400, .rate = 575000000},
{.prate = 0x00002800, .rate = 600000000},
{.prate = 0x00105800, .rate = 600000000},
{.prate = 0x00105C00, .rate = 625000000},
{.prate = 0x00002C00, .rate = 650000000},
{.prate = 0x00106000, .rate = 650000000},
{.prate = 0x00106400, .rate = 675000000},
{.prate = 0x00003000, .rate = 700000000},
{.prate = 0x00106800, .rate = 700000000},
{.prate = 0x00106C00, .rate = 725000000},
{.prate = 0x00003400, .rate = 750000000},
{.prate = 0x00107000, .rate = 750000000},
{.prate = 0x00107400, .rate = 775000000},
{.prate = 0x00003800, .rate = 800000000},
{.prate = 0x00107800, .rate = 800000000},
{.prate = 0x00107C00, .rate = 825000000},
{.prate = 0x00003C00, .rate = 850000000},
{.prate = 0x00108000, .rate = 850000000},
{.prate = 0x00108400, .rate = 875000000},
{.prate = 0x00004000, .rate = 900000000},
{.prate = 0x00108800, .rate = 900000000},
{.prate = 0x00108C00, .rate = 925000000},
{.prate = 0x00004400, .rate = 950000000},
{.prate = 0x00109000, .rate = 950000000},
{.prate = 0x00109400, .rate = 975000000},
{.prate = 0x00004800, .rate = 1000000000},
{.prate = 0x00109800, .rate = 1000000000},
};
struct {
unsigned long prate;
unsigned long drate;
} pddr_table[] = {
{.prate = 0x00100800, .drate = 44236800},
{.prate = 0x00100C00, .drate = 66355200},
{.prate = 0x00101000, .drate = 88473600},
{.prate = 0x00101400, .drate = 110592000},
{.prate = 0x00101800, .drate = 132710400},
{.prate = 0x00101C01, .drate = 154828800},
{.prate = 0x00102001, .drate = 176947200},
{.prate = 0x00102401, .drate = 199065600},
{.prate = 0x00102801, .drate = 221184000},
{.prate = 0x00102C01, .drate = 243302400},
{.prate = 0x00103001, .drate = 265420800},
{.prate = 0x00103401, .drate = 287539200},
{.prate = 0x00103801, .drate = 309657600},
{.prate = 0x00103C01, .drate = 331776000},
{.prate = 0x00104001, .drate = 353894400},
};
static int __init clk_init(void)
{
#ifdef CONFIG_PUV3_PM
u32 pllrate, divstatus = readl(PM_DIVSTATUS);
u32 pcgr_val = readl(PM_PCGR);
int i;
pcgr_val |= PM_PCGR_BCLKMME | PM_PCGR_BCLKH264E | PM_PCGR_BCLKH264D
| PM_PCGR_HECLK | PM_PCGR_HDCLK;
writel(pcgr_val, PM_PCGR);
pllrate = readl(PM_PLLSYSSTATUS);
/* lookup pmclk_table */
clk_mclk_clk.rate = 0;
for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) {
if (pllrate == pllrate_table[i].prate) {
clk_mclk_clk.rate = pllrate_table[i].rate;
break;
}
}
if (clk_mclk_clk.rate)
clk_bclk32_clk.rate = clk_mclk_clk.rate /
(((divstatus & 0x0000f000) >> 12) + 1);
pllrate = readl(PM_PLLDDRSTATUS);
/* lookup pddr_table */
clk_ddr_clk.rate = 0;
for (i = 0; i < ARRAY_SIZE(pddr_table); i++) {
if (pllrate == pddr_table[i].prate) {
clk_ddr_clk.rate = pddr_table[i].drate;
break;
}
}
pllrate = readl(PM_PLLVGASTATUS);
/* lookup pvga_table */
clk_vga_clk.rate = 0;
for (i = 0; i < ARRAY_SIZE(pllrate_table); i++) {
if (pllrate == pllrate_table[i].prate) {
clk_vga_clk.rate = pllrate_table[i].rate;
break;
}
}
if (clk_vga_clk.rate)
clk_vga_clk.rate = clk_vga_clk.rate /
(((divstatus & 0x00f00000) >> 20) + 1);
clk_register(&clk_vga_clk);
#endif
#ifdef CONFIG_ARCH_FPGA
clk_ddr_clk.rate = 33000000;
clk_mclk_clk.rate = 33000000;
clk_bclk32_clk.rate = 33000000;
#endif
clk_register(&clk_ddr_clk);
clk_register(&clk_mclk_clk);
clk_register(&clk_bclk32_clk);
clk_register(&clk_ost_clk);
return 0;
}
core_initcall(clk_init);

View File

@@ -0,0 +1,89 @@
/*
* linux/arch/unicore32/kernel/debug-macro.S
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Debugging macro include header
*/
#include <generated/asm-offsets.h>
#include <mach/hardware.h>
.macro put_word_ocd, rd, rx=r16
1001: movc \rx, p1.c0, #0
cand.a \rx, #2
bne 1001b
movc p1.c1, \rd, #1
.endm
#ifdef CONFIG_DEBUG_OCD
/* debug using UniCore On-Chip-Debugger */
.macro addruart, rx
.endm
.macro senduart, rd, rx
put_word_ocd \rd, \rx
.endm
.macro busyuart, rd, rx
.endm
.macro waituart, rd, rx
.endm
#else
#define UART_CLK_DEFAULT 3686400 * 20
/* Uartclk = MCLK/ 2, The MCLK on my board is 3686400 * 40 */
#define BAUD_RATE_DEFAULT 115200
/* The baud rate of the serial port */
#define UART_DIVISOR_DEFAULT (UART_CLK_DEFAULT \
/ (16 * BAUD_RATE_DEFAULT) - 1)
.macro addruart,rx
mrc p0, #0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
moveq \rx, #0xee000000 @ physical base address
movne \rx, #0x6e000000 @ virtual address
@ We probe for the active serial port here
@ However, now we assume UART0 is active: epip4d
@ We assume r1 and r2 can be clobbered.
movl r2, #UART_DIVISOR_DEFAULT
mov r1, #0x80
str r1, [\rx, #UART_LCR_OFFSET]
and r1, r2, #0xff00
mov r1, r1, lsr #8
str r1, [\rx, #UART_DLH_OFFSET]
and r1, r2, #0xff
str r1, [\rx, #UART_DLL_OFFSET]
mov r1, #0x7
str r1, [\rx, #UART_FCR_OFFSET]
mov r1, #0x3
str r1, [\rx, #UART_LCR_OFFSET]
mov r1, #0x0
str r1, [\rx, #UART_IER_OFFSET]
.endm
.macro senduart,rd,rx
str \rd, [\rx, #UART_THR_OFFSET]
.endm
.macro waituart,rd,rx
1001: ldr \rd, [\rx, #UART_LSR_OFFSET]
tst \rd, #UART_LSR_THRE
beq 1001b
.endm
.macro busyuart,rd,rx
1001: ldr \rd, [\rx, #UART_LSR_OFFSET]
tst \rd, #UART_LSR_TEMT
bne 1001b
.endm
#endif

View File

@@ -0,0 +1,85 @@
/*
* linux/arch/unicore32/kernel/debug.S
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 32-bit debugging code
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
.text
/*
* Some debugging routines (useful if you've got MM problems and
* printk isn't working). For DEBUGGING ONLY!!! Do not leave
* references to these in a production kernel!
*/
#include "debug-macro.S"
/*
* Useful debugging routines
*/
ENTRY(printhex8)
mov r1, #8
b printhex
ENDPROC(printhex8)
ENTRY(printhex4)
mov r1, #4
b printhex
ENDPROC(printhex4)
ENTRY(printhex2)
mov r1, #2
printhex: adr r2, hexbuf
add r3, r2, r1
mov r1, #0
stb r1, [r3]
1: and r1, r0, #15
mov r0, r0 >> #4
csub.a r1, #10
beg 2f
add r1, r1, #'0' - 'a' + 10
2: add r1, r1, #'a' - 10
stb.w r1, [r3+], #-1
cxor.a r3, r2
bne 1b
mov r0, r2
b printascii
ENDPROC(printhex2)
.ltorg
ENTRY(printascii)
addruart r3
b 2f
1: waituart r2, r3
senduart r1, r3
busyuart r2, r3
cxor.a r1, #'\n'
cmoveq r1, #'\r'
beq 1b
2: cxor.a r0, #0
beq 3f
ldb.w r1, [r0]+, #1
cxor.a r1, #0
bne 1b
3: mov pc, lr
ENDPROC(printascii)
ENTRY(printch)
addruart r3
mov r1, r0
mov r0, #0
b 1b
ENDPROC(printch)
hexbuf: .space 16

182
arch/unicore32/kernel/dma.c Normal file
View File

@@ -0,0 +1,182 @@
/*
* linux/arch/unicore32/kernel/dma.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/dma.h>
struct dma_channel {
char *name;
puv3_dma_prio prio;
void (*irq_handler)(int, void *);
void (*err_handler)(int, void *);
void *data;
};
static struct dma_channel dma_channels[MAX_DMA_CHANNELS];
int puv3_request_dma(char *name, puv3_dma_prio prio,
void (*irq_handler)(int, void *),
void (*err_handler)(int, void *),
void *data)
{
unsigned long flags;
int i, found = 0;
/* basic sanity checks */
if (!name)
return -EINVAL;
local_irq_save(flags);
do {
/* try grabbing a DMA channel with the requested priority */
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
if ((dma_channels[i].prio == prio) &&
!dma_channels[i].name) {
found = 1;
break;
}
}
/* if requested prio group is full, try a hier priority */
} while (!found && prio--);
if (found) {
dma_channels[i].name = name;
dma_channels[i].irq_handler = irq_handler;
dma_channels[i].err_handler = err_handler;
dma_channels[i].data = data;
} else {
printk(KERN_WARNING "No more available DMA channels for %s\n",
name);
i = -ENODEV;
}
local_irq_restore(flags);
return i;
}
EXPORT_SYMBOL(puv3_request_dma);
void puv3_free_dma(int dma_ch)
{
unsigned long flags;
if (!dma_channels[dma_ch].name) {
printk(KERN_CRIT
"%s: trying to free channel %d which is already freed\n",
__func__, dma_ch);
return;
}
local_irq_save(flags);
dma_channels[dma_ch].name = NULL;
dma_channels[dma_ch].err_handler = NULL;
local_irq_restore(flags);
}
EXPORT_SYMBOL(puv3_free_dma);
static irqreturn_t dma_irq_handler(int irq, void *dev_id)
{
int i, dint;
dint = readl(DMAC_ITCSR);
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
if (dint & DMAC_CHANNEL(i)) {
struct dma_channel *channel = &dma_channels[i];
/* Clear TC interrupt of channel i */
writel(DMAC_CHANNEL(i), DMAC_ITCCR);
writel(0, DMAC_ITCCR);
if (channel->name && channel->irq_handler) {
channel->irq_handler(i, channel->data);
} else {
/*
* IRQ for an unregistered DMA channel:
* let's clear the interrupts and disable it.
*/
printk(KERN_WARNING "spurious IRQ for"
" DMA channel %d\n", i);
}
}
}
return IRQ_HANDLED;
}
static irqreturn_t dma_err_handler(int irq, void *dev_id)
{
int i, dint;
dint = readl(DMAC_IESR);
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
if (dint & DMAC_CHANNEL(i)) {
struct dma_channel *channel = &dma_channels[i];
/* Clear Err interrupt of channel i */
writel(DMAC_CHANNEL(i), DMAC_IECR);
writel(0, DMAC_IECR);
if (channel->name && channel->err_handler) {
channel->err_handler(i, channel->data);
} else {
/*
* IRQ for an unregistered DMA channel:
* let's clear the interrupts and disable it.
*/
printk(KERN_WARNING "spurious IRQ for"
" DMA channel %d\n", i);
}
}
}
return IRQ_HANDLED;
}
int __init puv3_init_dma(void)
{
int i, ret;
/* dma channel priorities on v8 processors:
* ch 0 - 1 <--> (0) DMA_PRIO_HIGH
* ch 2 - 3 <--> (1) DMA_PRIO_MEDIUM
* ch 4 - 5 <--> (2) DMA_PRIO_LOW
*/
for (i = 0; i < MAX_DMA_CHANNELS; i++) {
puv3_stop_dma(i);
dma_channels[i].name = NULL;
dma_channels[i].prio = min((i & 0x7) >> 1, DMA_PRIO_LOW);
}
ret = request_irq(IRQ_DMA, dma_irq_handler, 0, "DMA", NULL);
if (ret) {
printk(KERN_CRIT "Can't register IRQ for DMA\n");
return ret;
}
ret = request_irq(IRQ_DMAERR, dma_err_handler, 0, "DMAERR", NULL);
if (ret) {
printk(KERN_CRIT "Can't register IRQ for DMAERR\n");
free_irq(IRQ_DMA, "DMA");
return ret;
}
return 0;
}
postcore_initcall(puv3_init_dma);

View File

@@ -0,0 +1,55 @@
/*
* linux/arch/unicore32/kernel/early_printk.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/console.h>
#include <linux/init.h>
#include <linux/string.h>
#include <mach/ocd.h>
/* On-Chip-Debugger functions */
static void early_ocd_write(struct console *con, const char *s, unsigned n)
{
while (*s && n-- > 0) {
if (*s == '\n')
ocd_putc((int)'\r');
ocd_putc((int)*s);
s++;
}
}
static struct console early_ocd_console = {
.name = "earlyocd",
.write = early_ocd_write,
.flags = CON_PRINTBUFFER,
.index = -1,
};
static int __init setup_early_printk(char *buf)
{
int keep_early;
if (!buf || early_console)
return 0;
if (strstr(buf, "keep"))
keep_early = 1;
early_console = &early_ocd_console;
if (keep_early)
early_console->flags &= ~CON_BOOT;
else
early_console->flags |= CON_BOOT;
register_console(early_console);
return 0;
}
early_param("earlyprintk", setup_early_printk);

View File

@@ -0,0 +1,38 @@
/*
* linux/arch/unicore32/kernel/elf.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/personality.h>
#include <linux/binfmts.h>
#include <linux/elf.h>
int elf_check_arch(const struct elf32_hdr *x)
{
/* Make sure it's an UniCore executable */
if (x->e_machine != EM_UNICORE)
return 0;
/* Make sure the entry address is reasonable */
if (x->e_entry & 3)
return 0;
return 1;
}
EXPORT_SYMBOL(elf_check_arch);
void elf_set_personality(const struct elf32_hdr *x)
{
unsigned int personality = PER_LINUX;
set_personality(personality);
}
EXPORT_SYMBOL(elf_set_personality);

View File

@@ -0,0 +1,805 @@
/*
* linux/arch/unicore32/kernel/entry.S
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Low-level vector interface routines
*/
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/errno.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
#include <asm/unistd.h>
#include <generated/asm-offsets.h>
#include "debug-macro.S"
@
@ Most of the stack format comes from struct pt_regs, but with
@ the addition of 8 bytes for storing syscall args 5 and 6.
@
#define S_OFF 8
/*
* The SWI code relies on the fact that R0 is at the bottom of the stack
* (due to slow/fast restore user regs).
*/
#if S_R0 != 0
#error "Please fix"
#endif
.macro zero_fp
#ifdef CONFIG_FRAME_POINTER
mov fp, #0
#endif
.endm
.macro alignment_trap, rtemp
#ifdef CONFIG_ALIGNMENT_TRAP
ldw \rtemp, .LCcralign
ldw \rtemp, [\rtemp]
movc p0.c1, \rtemp, #0
#endif
.endm
.macro load_user_sp_lr, rd, rtemp, offset = 0
mov \rtemp, asr
xor \rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE)
mov.a asr, \rtemp @ switch to the SUSR mode
ldw sp, [\rd+], #\offset @ load sp_user
ldw lr, [\rd+], #\offset + 4 @ load lr_user
xor \rtemp, \rtemp, #(PRIV_MODE ^ SUSR_MODE)
mov.a asr, \rtemp @ switch back to the PRIV mode
.endm
.macro priv_exit, rpsr
mov.a bsr, \rpsr
ldm.w (r0 - r15), [sp]+
ldm.b (r16 - pc), [sp]+ @ load r0 - pc, asr
.endm
.macro restore_user_regs, fast = 0, offset = 0
ldw r1, [sp+], #\offset + S_PSR @ get calling asr
ldw lr, [sp+], #\offset + S_PC @ get pc
mov.a bsr, r1 @ save in bsr_priv
.if \fast
add sp, sp, #\offset + S_R1 @ r0 is syscall return value
ldm.w (r1 - r15), [sp]+ @ get calling r1 - r15
ldur (r16 - lr), [sp]+ @ get calling r16 - lr
.else
ldm.w (r0 - r15), [sp]+ @ get calling r0 - r15
ldur (r16 - lr), [sp]+ @ get calling r16 - lr
.endif
nop
add sp, sp, #S_FRAME_SIZE - S_R16
mov.a pc, lr @ return
@ and move bsr_priv into asr
.endm
.macro get_thread_info, rd
mov \rd, sp >> #13
mov \rd, \rd << #13
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
ldw \base, =(PKUNITY_INTC_BASE)
ldw \irqstat, [\base+], #0xC @ INTC_ICIP
ldw \tmp, [\base+], #0x4 @ INTC_ICMR
and.a \irqstat, \irqstat, \tmp
beq 1001f
cntlz \irqnr, \irqstat
rsub \irqnr, \irqnr, #31
1001: /* EQ will be set if no irqs pending */
.endm
#ifdef CONFIG_DEBUG_LL
.macro printreg, reg, temp
adr \temp, 901f
stm (r0-r3), [\temp]+
stw lr, [\temp+], #0x10
mov r0, \reg
b.l printhex8
mov r0, #':'
b.l printch
mov r0, pc
b.l printhex8
adr r0, 902f
b.l printascii
adr \temp, 901f
ldm (r0-r3), [\temp]+
ldw lr, [\temp+], #0x10
b 903f
901: .word 0, 0, 0, 0, 0 @ r0-r3, lr
902: .asciz ": epip4d\n"
.align
903:
.endm
#endif
/*
* These are the registers used in the syscall handler, and allow us to
* have in theory up to 7 arguments to a function - r0 to r6.
*
* Note that tbl == why is intentional.
*
* We must set at least "tsk" and "why" when calling ret_with_reschedule.
*/
scno .req r21 @ syscall number
tbl .req r22 @ syscall table pointer
why .req r22 @ Linux syscall (!= 0)
tsk .req r23 @ current thread_info
/*
* Interrupt handling. Preserves r17, r18, r19
*/
.macro intr_handler
1: get_irqnr_and_base r0, r6, r5, lr
beq 2f
mov r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adr lr, 1b
b asm_do_IRQ
2:
.endm
/*
* PRIV mode handlers
*/
.macro priv_entry
sub sp, sp, #(S_FRAME_SIZE - 4)
stm (r1 - r15), [sp]+
add r5, sp, #S_R15
stm (r16 - r28), [r5]+
ldm (r1 - r3), [r0]+
add r5, sp, #S_SP - 4 @ here for interlock avoidance
mov r4, #-1 @ "" "" "" ""
add r0, sp, #(S_FRAME_SIZE - 4)
stw.w r1, [sp+], #-4 @ save the "real" r0 copied
@ from the exception stack
mov r1, lr
@
@ We are now ready to fill in the remaining blanks on the stack:
@
@ r0 - sp_priv
@ r1 - lr_priv
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - bsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
stm (r0 - r4), [r5]+
.endm
/*
* User mode handlers
*
*/
.macro user_entry
sub sp, sp, #S_FRAME_SIZE
stm (r1 - r15), [sp+]
add r4, sp, #S_R16
stm (r16 - r28), [r4]+
ldm (r1 - r3), [r0]+
add r0, sp, #S_PC @ here for interlock avoidance
mov r4, #-1 @ "" "" "" ""
stw r1, [sp] @ save the "real" r0 copied
@ from the exception stack
@
@ We are now ready to fill in the remaining blanks on the stack:
@
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - bsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
@ Also, separately save sp_user and lr_user
@
stm (r2 - r4), [r0]+
stur (sp, lr), [r0-]
@
@ Enable the alignment trap while in kernel mode
@
alignment_trap r0
@
@ Clear FP to mark the first stack frame
@
zero_fp
.endm
.text
@
@ __invalid - generic code for failed exception
@ (re-entrant version of handlers)
@
__invalid:
sub sp, sp, #S_FRAME_SIZE
stm (r1 - r15), [sp+]
add r1, sp, #S_R16
stm (r16 - r28, sp, lr), [r1]+
zero_fp
ldm (r4 - r6), [r0]+
add r0, sp, #S_PC @ here for interlock avoidance
mov r7, #-1 @ "" "" "" ""
stw r4, [sp] @ save preserved r0
stm (r5 - r7), [r0]+ @ lr_<exception>,
@ asr_<exception>, "old_r0"
mov r0, sp
mov r1, asr
b bad_mode
ENDPROC(__invalid)
.align 5
__dabt_priv:
priv_entry
@
@ get ready to re-enable interrupts if appropriate
@
mov r17, asr
cand.a r3, #PSR_I_BIT
bne 1f
andn r17, r17, #PSR_I_BIT
1:
@
@ Call the processor-specific abort handler:
@
@ r2 - aborted context pc
@ r3 - aborted context asr
@
@ The abort handler must return the aborted address in r0, and
@ the fault status register in r1.
@
movc r1, p0.c3, #0 @ get FSR
movc r0, p0.c4, #0 @ get FAR
@
@ set desired INTR state, then call main handler
@
mov.a asr, r17
mov r2, sp
b.l do_DataAbort
@
@ INTRs off again before pulling preserved data off the stack
@
disable_irq r0
@
@ restore BSR and restart the instruction
@
ldw r2, [sp+], #S_PSR
priv_exit r2 @ return from exception
ENDPROC(__dabt_priv)
.align 5
__intr_priv:
priv_entry
intr_handler
mov r0, #0 @ epip4d
movc p0.c5, r0, #14
nop; nop; nop; nop; nop; nop; nop; nop
ldw r4, [sp+], #S_PSR @ irqs are already disabled
priv_exit r4 @ return from exception
ENDPROC(__intr_priv)
.ltorg
.align 5
__extn_priv:
priv_entry
mov r0, sp @ struct pt_regs *regs
mov r1, asr
b bad_mode @ not supported
ENDPROC(__extn_priv)
.align 5
__pabt_priv:
priv_entry
@
@ re-enable interrupts if appropriate
@
mov r17, asr
cand.a r3, #PSR_I_BIT
bne 1f
andn r17, r17, #PSR_I_BIT
1:
@
@ set args, then call main handler
@
@ r0 - address of faulting instruction
@ r1 - pointer to registers on stack
@
mov r0, r2 @ pass address of aborted instruction
mov r1, #5
mov.a asr, r17
mov r2, sp @ regs
b.l do_PrefetchAbort @ call abort handler
@
@ INTRs off again before pulling preserved data off the stack
@
disable_irq r0
@
@ restore BSR and restart the instruction
@
ldw r2, [sp+], #S_PSR
priv_exit r2 @ return from exception
ENDPROC(__pabt_priv)
.align 5
.LCcralign:
.word cr_alignment
.align 5
__dabt_user:
user_entry
#ifdef CONFIG_UNICORE_FPU_F64
cff ip, s31
cand.a ip, #0x08000000 @ FPU execption traps?
beq 209f
ldw ip, [sp+], #S_PC
add ip, ip, #4
stw ip, [sp+], #S_PC
@
@ fall through to the emulation code, which returns using r19 if
@ it has emulated the instruction, or the more conventional lr
@ if we are to treat this as a real extended instruction
@
@ r0 - instruction
@
1: ldw.u r0, [r2]
adr r19, ret_from_exception
adr lr, 209f
@
@ fallthrough to call do_uc_f64
@
/*
* Check whether the instruction is a co-processor instruction.
* If yes, we need to call the relevant co-processor handler.
*
* Note that we don't do a full check here for the co-processor
* instructions; all instructions with bit 27 set are well
* defined. The only instructions that should fault are the
* co-processor instructions.
*
* Emulators may wish to make use of the following registers:
* r0 = instruction opcode.
* r2 = PC
* r19 = normal "successful" return address
* r20 = this threads thread_info structure.
* lr = unrecognised instruction return address
*/
get_thread_info r20 @ get current thread
and r8, r0, #0x00003c00 @ mask out CP number
mov r7, #1
stb r7, [r20+], #TI_USED_CP + 2 @ set appropriate used_cp[]
@ F64 hardware support entry point.
@ r0 = faulted instruction
@ r19 = return address
@ r20 = fp_state
enable_irq r4
add r20, r20, #TI_FPSTATE @ r20 = workspace
cff r1, s31 @ get fpu FPSCR
andn r2, r1, #0x08000000
ctf r2, s31 @ clear 27 bit
mov r2, sp @ nothing stacked - regdump is at TOS
mov lr, r19 @ setup for a return to the user code
@ Now call the C code to package up the bounce to the support code
@ r0 holds the trigger instruction
@ r1 holds the FPSCR value
@ r2 pointer to register dump
b ucf64_exchandler
209:
#endif
@
@ Call the processor-specific abort handler:
@
@ r2 - aborted context pc
@ r3 - aborted context asr
@
@ The abort handler must return the aborted address in r0, and
@ the fault status register in r1.
@
movc r1, p0.c3, #0 @ get FSR
movc r0, p0.c4, #0 @ get FAR
@
@ INTRs on, then call the main handler
@
enable_irq r2
mov r2, sp
adr lr, ret_from_exception
b do_DataAbort
ENDPROC(__dabt_user)
.align 5
__intr_user:
user_entry
get_thread_info tsk
intr_handler
mov why, #0
b ret_to_user
ENDPROC(__intr_user)
.ltorg
.align 5
__extn_user:
user_entry
mov r0, sp
mov r1, asr
b bad_mode
ENDPROC(__extn_user)
.align 5
__pabt_user:
user_entry
mov r0, r2 @ pass address of aborted instruction.
mov r1, #5
enable_irq r1 @ Enable interrupts
mov r2, sp @ regs
b.l do_PrefetchAbort @ call abort handler
/* fall through */
/*
* This is the return code to user mode for abort handlers
*/
ENTRY(ret_from_exception)
get_thread_info tsk
mov why, #0
b ret_to_user
ENDPROC(__pabt_user)
ENDPROC(ret_from_exception)
/*
* Register switch for UniCore V2 processors
* r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
* previous and next are guaranteed not to be the same.
*/
ENTRY(__switch_to)
add ip, r1, #TI_CPU_SAVE
stm.w (r4 - r15), [ip]+
stm.w (r16 - r27, sp, lr), [ip]+
#ifdef CONFIG_UNICORE_FPU_F64
add ip, r1, #TI_FPSTATE
sfm.w (f0 - f7 ), [ip]+
sfm.w (f8 - f15), [ip]+
sfm.w (f16 - f23), [ip]+
sfm.w (f24 - f31), [ip]+
cff r4, s31
stw r4, [ip]
add ip, r2, #TI_FPSTATE
lfm.w (f0 - f7 ), [ip]+
lfm.w (f8 - f15), [ip]+
lfm.w (f16 - f23), [ip]+
lfm.w (f24 - f31), [ip]+
ldw r4, [ip]
ctf r4, s31
#endif
add ip, r2, #TI_CPU_SAVE
ldm.w (r4 - r15), [ip]+
ldm (r16 - r27, sp, pc), [ip]+ @ Load all regs saved previously
ENDPROC(__switch_to)
.align 5
/*
* This is the fast syscall return path. We do as little as
* possible here, and this includes saving r0 back into the PRIV
* stack.
*/
ret_fast_syscall:
disable_irq r1 @ disable interrupts
ldw r1, [tsk+], #TI_FLAGS
cand.a r1, #_TIF_WORK_MASK
bne fast_work_pending
@ fast_restore_user_regs
restore_user_regs fast = 1, offset = S_OFF
/*
* Ok, we need to do extra processing, enter the slow path.
*/
fast_work_pending:
stw.w r0, [sp+], #S_R0+S_OFF @ returned r0
work_pending:
cand.a r1, #_TIF_NEED_RESCHED
bne work_resched
mov r0, sp @ 'regs'
mov r2, why @ 'syscall'
cand.a r1, #_TIF_SIGPENDING @ delivering a signal?
cmovne why, #0 @ prevent further restarts
b.l do_notify_resume
b ret_slow_syscall @ Check work again
work_resched:
b.l schedule
/*
* "slow" syscall return path. "why" tells us if this was a real syscall.
*/
ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq r1 @ disable interrupts
get_thread_info tsk @ epip4d, one path error?!
ldw r1, [tsk+], #TI_FLAGS
cand.a r1, #_TIF_WORK_MASK
bne work_pending
no_work_pending:
@ slow_restore_user_regs
restore_user_regs fast = 0, offset = 0
ENDPROC(ret_to_user)
/*
* This is how we return from a fork.
*/
ENTRY(ret_from_fork)
b.l schedule_tail
b ret_slow_syscall
ENDPROC(ret_from_fork)
ENTRY(ret_from_kernel_thread)
b.l schedule_tail
mov r0, r5
adr lr, ret_slow_syscall
mov pc, r4
ENDPROC(ret_from_kernel_thread)
/*=============================================================================
* SWI handler
*-----------------------------------------------------------------------------
*/
.align 5
ENTRY(vector_swi)
sub sp, sp, #S_FRAME_SIZE
stm (r0 - r15), [sp]+ @ Calling r0 - r15
add r8, sp, #S_R16
stm (r16 - r28), [r8]+ @ Calling r16 - r28
add r8, sp, #S_PC
stur (sp, lr), [r8-] @ Calling sp, lr
mov r8, bsr @ called from non-REAL mode
stw lr, [sp+], #S_PC @ Save calling PC
stw r8, [sp+], #S_PSR @ Save ASR
stw r0, [sp+], #S_OLD_R0 @ Save OLD_R0
zero_fp
/*
* Get the system call number.
*/
sub ip, lr, #4
ldw.u scno, [ip] @ get SWI instruction
#ifdef CONFIG_ALIGNMENT_TRAP
ldw ip, __cr_alignment
ldw ip, [ip]
movc p0.c1, ip, #0 @ update control register
#endif
enable_irq ip
get_thread_info tsk
ldw tbl, =sys_call_table @ load syscall table pointer
andn scno, scno, #0xff000000 @ mask off SWI op-code
andn scno, scno, #0x00ff0000 @ mask off SWI op-code
stm.w (r4, r5), [sp-] @ push fifth and sixth args
ldw ip, [tsk+], #TI_FLAGS @ check for syscall tracing
cand.a ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
bne __sys_trace
csub.a scno, #__NR_syscalls @ check upper syscall limit
adr lr, ret_fast_syscall @ return address
bea 1f
ldw pc, [tbl+], scno << #2 @ call sys_* routine
1:
add r1, sp, #S_OFF
2: mov why, #0 @ no longer a real syscall
b sys_ni_syscall @ not private func
/*
* This is the really slow path. We're going to be doing
* context switches, and waiting for our parent to respond.
*/
__sys_trace:
mov r2, scno
add r1, sp, #S_OFF
mov r0, #0 @ trace entry [IP = 0]
b.l syscall_trace
adr lr, __sys_trace_return @ return address
mov scno, r0 @ syscall number (possibly new)
add r1, sp, #S_R0 + S_OFF @ pointer to regs
csub.a scno, #__NR_syscalls @ check upper syscall limit
bea 2b
ldm (r0 - r3), [r1]+ @ have to reload r0 - r3
ldw pc, [tbl+], scno << #2 @ call sys_* routine
__sys_trace_return:
stw.w r0, [sp+], #S_R0 + S_OFF @ save returned r0
mov r2, scno
mov r1, sp
mov r0, #1 @ trace exit [IP = 1]
b.l syscall_trace
b ret_slow_syscall
.align 5
#ifdef CONFIG_ALIGNMENT_TRAP
.type __cr_alignment, #object
__cr_alignment:
.word cr_alignment
#endif
.ltorg
ENTRY(sys_rt_sigreturn)
add r0, sp, #S_OFF
mov why, #0 @ prevent syscall restart handling
b __sys_rt_sigreturn
ENDPROC(sys_rt_sigreturn)
__INIT
/*
* Vector stubs.
*
* This code is copied to 0xffff0200 so we can use branches in the
* vectors, rather than ldr's. Note that this code must not
* exceed 0x300 bytes.
*
* Common stub entry macro:
* Enter in INTR mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC
*
* SP points to a minimal amount of processor-private memory, the address
* of which is copied into r0 for the mode specific abort handler.
*/
.macro vector_stub, name, mode
.align 5
vector_\name:
@
@ Save r0, lr_<exception> (parent PC) and bsr_<exception>
@ (parent ASR)
@
stw r0, [sp]
stw lr, [sp+], #4 @ save r0, lr
mov lr, bsr
stw lr, [sp+], #8 @ save bsr
@
@ Prepare for PRIV mode. INTRs remain disabled.
@
mov r0, asr
xor r0, r0, #(\mode ^ PRIV_MODE)
mov.a bsr, r0
@
@ the branch table must immediately follow this code
@
and lr, lr, #0x03
add lr, lr, #1
mov r0, sp
ldw lr, [pc+], lr << #2
mov.a pc, lr @ branch to handler in PRIV mode
ENDPROC(vector_\name)
.align 2
@ handler addresses follow this label
.endm
.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub intr, INTR_MODE
.long __intr_user @ 0 (USER)
.long __invalid @ 1
.long __invalid @ 2
.long __intr_priv @ 3 (PRIV)
/*
* Data abort dispatcher
* Enter in ABT mode, bsr = USER ASR, lr = USER PC
*/
vector_stub dabt, ABRT_MODE
.long __dabt_user @ 0 (USER)
.long __invalid @ 1
.long __invalid @ 2 (INTR)
.long __dabt_priv @ 3 (PRIV)
/*
* Prefetch abort dispatcher
* Enter in ABT mode, bsr = USER ASR, lr = USER PC
*/
vector_stub pabt, ABRT_MODE
.long __pabt_user @ 0 (USER)
.long __invalid @ 1
.long __invalid @ 2 (INTR)
.long __pabt_priv @ 3 (PRIV)
/*
* Undef instr entry dispatcher
* Enter in EXTN mode, bsr = PRIV/USER ASR, lr = PRIV/USER PC
*/
vector_stub extn, EXTN_MODE
.long __extn_user @ 0 (USER)
.long __invalid @ 1
.long __invalid @ 2 (INTR)
.long __extn_priv @ 3 (PRIV)
/*
* We group all the following data together to optimise
* for CPUs with separate I & D caches.
*/
.align 5
.LCvswi:
.word vector_swi
.globl __stubs_end
__stubs_end:
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
.globl __vectors_start
__vectors_start:
jepriv SYS_ERROR0
b vector_extn + stubs_offset
ldw pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
jepriv SYS_ERROR0
b vector_intr + stubs_offset
jepriv SYS_ERROR0
.globl __vectors_end
__vectors_end:
.data
.globl cr_alignment
.globl cr_no_alignment
cr_alignment:
.space 4
cr_no_alignment:
.space 4

View File

@@ -0,0 +1,126 @@
/*
* linux/arch/unicore32/kernel/fpu-ucf64.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <asm/fpu-ucf64.h>
/*
* A special flag to tell the normalisation code not to normalise.
*/
#define F64_NAN_FLAG 0x100
/*
* A bit pattern used to indicate the initial (unset) value of the
* exception mask, in case nothing handles an instruction. This
* doesn't include the NAN flag, which get masked out before
* we check for an error.
*/
#define F64_EXCEPTION_ERROR ((u32)-1 & ~F64_NAN_FLAG)
/*
* Since we aren't building with -mfpu=f64, we need to code
* these instructions using their MRC/MCR equivalents.
*/
#define f64reg(_f64_) #_f64_
#define cff(_f64_) ({ \
u32 __v; \
asm("cff %0, " f64reg(_f64_) "@ fmrx %0, " #_f64_ \
: "=r" (__v) : : "cc"); \
__v; \
})
#define ctf(_f64_, _var_) \
asm("ctf %0, " f64reg(_f64_) "@ fmxr " #_f64_ ", %0" \
: : "r" (_var_) : "cc")
/*
* Raise a SIGFPE for the current process.
* sicode describes the signal being raised.
*/
void ucf64_raise_sigfpe(unsigned int sicode, struct pt_regs *regs)
{
siginfo_t info;
memset(&info, 0, sizeof(info));
info.si_signo = SIGFPE;
info.si_code = sicode;
info.si_addr = (void __user *)(instruction_pointer(regs) - 4);
/*
* This is the same as NWFPE, because it's not clear what
* this is used for
*/
current->thread.error_code = 0;
current->thread.trap_no = 6;
send_sig_info(SIGFPE, &info, current);
}
/*
* Handle exceptions of UniCore-F64.
*/
void ucf64_exchandler(u32 inst, u32 fpexc, struct pt_regs *regs)
{
u32 tmp = fpexc;
u32 exc = F64_EXCEPTION_ERROR & fpexc;
pr_debug("UniCore-F64: instruction %08x fpscr %08x\n",
inst, fpexc);
if (exc & FPSCR_CMPINSTR_BIT) {
if (exc & FPSCR_CON)
tmp |= FPSCR_CON;
else
tmp &= ~(FPSCR_CON);
exc &= ~(FPSCR_CMPINSTR_BIT | FPSCR_CON);
} else {
pr_debug(KERN_ERR "UniCore-F64 Error: unhandled exceptions\n");
pr_debug(KERN_ERR "UniCore-F64 FPSCR 0x%08x INST 0x%08x\n",
cff(FPSCR), inst);
ucf64_raise_sigfpe(0, regs);
return;
}
/*
* Update the FPSCR with the additional exception flags.
* Comparison instructions always return at least one of
* these flags set.
*/
tmp &= ~(FPSCR_TRAP | FPSCR_IOS | FPSCR_OFS | FPSCR_UFS |
FPSCR_IXS | FPSCR_HIS | FPSCR_IOC | FPSCR_OFC |
FPSCR_UFC | FPSCR_IXC | FPSCR_HIC);
tmp |= exc;
ctf(FPSCR, tmp);
}
/*
* F64 support code initialisation.
*/
static int __init ucf64_init(void)
{
ctf(FPSCR, 0x0); /* FPSCR_UFE | FPSCR_NDE perhaps better */
printk(KERN_INFO "Enable UniCore-F64 support.\n");
return 0;
}
late_initcall(ucf64_init);

View File

@@ -0,0 +1,122 @@
/*
* linux/arch/unicore32/kernel/gpio.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* in FPGA, no GPIO support */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/hardware.h>
#ifdef CONFIG_LEDS
#include <linux/leds.h>
#include <linux/platform_device.h>
static const struct gpio_led puv3_gpio_leds[] = {
{ .name = "cpuhealth", .gpio = GPO_CPU_HEALTH, .active_low = 0,
.default_trigger = "heartbeat", },
{ .name = "hdd_led", .gpio = GPO_HDD_LED, .active_low = 1,
.default_trigger = "ide-disk", },
};
static const struct gpio_led_platform_data puv3_gpio_led_data = {
.num_leds = ARRAY_SIZE(puv3_gpio_leds),
.leds = (void *) puv3_gpio_leds,
};
static struct platform_device puv3_gpio_gpio_leds = {
.name = "leds-gpio",
.id = -1,
.dev = {
.platform_data = (void *) &puv3_gpio_led_data,
}
};
static int __init puv3_gpio_leds_init(void)
{
platform_device_register(&puv3_gpio_gpio_leds);
return 0;
}
device_initcall(puv3_gpio_leds_init);
#endif
static int puv3_gpio_get(struct gpio_chip *chip, unsigned offset)
{
return readl(GPIO_GPLR) & GPIO_GPIO(offset);
}
static void puv3_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
if (value)
writel(GPIO_GPIO(offset), GPIO_GPSR);
else
writel(GPIO_GPIO(offset), GPIO_GPCR);
}
static int puv3_direction_input(struct gpio_chip *chip, unsigned offset)
{
unsigned long flags;
local_irq_save(flags);
writel(readl(GPIO_GPDR) & ~GPIO_GPIO(offset), GPIO_GPDR);
local_irq_restore(flags);
return 0;
}
static int puv3_direction_output(struct gpio_chip *chip, unsigned offset,
int value)
{
unsigned long flags;
local_irq_save(flags);
puv3_gpio_set(chip, offset, value);
writel(readl(GPIO_GPDR) | GPIO_GPIO(offset), GPIO_GPDR);
local_irq_restore(flags);
return 0;
}
static struct gpio_chip puv3_gpio_chip = {
.label = "gpio",
.direction_input = puv3_direction_input,
.direction_output = puv3_direction_output,
.set = puv3_gpio_set,
.get = puv3_gpio_get,
.base = 0,
.ngpio = GPIO_MAX + 1,
};
void __init puv3_init_gpio(void)
{
writel(GPIO_DIR, GPIO_GPDR);
#if defined(CONFIG_PUV3_NB0916) || defined(CONFIG_PUV3_SMW0919) \
|| defined(CONFIG_PUV3_DB0913)
gpio_set_value(GPO_WIFI_EN, 1);
gpio_set_value(GPO_HDD_LED, 1);
gpio_set_value(GPO_VGA_EN, 1);
gpio_set_value(GPO_LCD_EN, 1);
gpio_set_value(GPO_CAM_PWR_EN, 0);
gpio_set_value(GPO_LCD_VCC_EN, 1);
gpio_set_value(GPO_SOFT_OFF, 1);
gpio_set_value(GPO_BT_EN, 1);
gpio_set_value(GPO_FAN_ON, 0);
gpio_set_value(GPO_SPKR, 0);
gpio_set_value(GPO_CPU_HEALTH, 1);
gpio_set_value(GPO_LAN_SEL, 1);
/*
* DO NOT modify the GPO_SET_V1 and GPO_SET_V2 in kernel
* gpio_set_value(GPO_SET_V1, 1);
* gpio_set_value(GPO_SET_V2, 1);
*/
#endif
gpiochip_add(&puv3_gpio_chip);
}

View File

@@ -0,0 +1,252 @@
/*
* linux/arch/unicore32/kernel/head.S
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/assembler.h>
#include <asm/ptrace.h>
#include <generated/asm-offsets.h>
#include <asm/memory.h>
#include <asm/thread_info.h>
#include <asm/hwdef-copro.h>
#include <asm/pgtable-hwdef.h>
#if (PHYS_OFFSET & 0x003fffff)
#error "PHYS_OFFSET must be at an even 4MiB boundary!"
#endif
#define KERNEL_RAM_VADDR (PAGE_OFFSET + KERNEL_IMAGE_START)
#define KERNEL_RAM_PADDR (PHYS_OFFSET + KERNEL_IMAGE_START)
#define KERNEL_PGD_PADDR (KERNEL_RAM_PADDR - 0x1000)
#define KERNEL_PGD_VADDR (KERNEL_RAM_VADDR - 0x1000)
#define KERNEL_START KERNEL_RAM_VADDR
#define KERNEL_END _end
/*
* swapper_pg_dir is the virtual address of the initial page table.
* We place the page tables 4K below KERNEL_RAM_VADDR. Therefore, we must
* make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect
* the least significant 16 bits to be 0x8000, but we could probably
* relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x1000.
*/
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
#endif
.globl swapper_pg_dir
.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x1000
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*/
__HEAD
ENTRY(stext)
@ set asr
mov r0, #PRIV_MODE @ ensure priv mode
or r0, #PSR_R_BIT | PSR_I_BIT @ disable irqs
mov.a asr, r0
@ process identify
movc r0, p0.c0, #0 @ cpuid
movl r1, 0xff00ffff @ mask
movl r2, 0x4d000863 @ value
and r0, r1, r0
cxor.a r0, r2
bne __error_p @ invalid processor id
/*
* Clear the 4K level 1 swapper page table
*/
movl r0, #KERNEL_PGD_PADDR @ page table address
mov r1, #0
add r2, r0, #0x1000
101: stw.w r1, [r0]+, #4
stw.w r1, [r0]+, #4
stw.w r1, [r0]+, #4
stw.w r1, [r0]+, #4
cxor.a r0, r2
bne 101b
movl r4, #KERNEL_PGD_PADDR @ page table address
mov r7, #PMD_TYPE_SECT | PMD_PRESENT @ page size: section
or r7, r7, #PMD_SECT_CACHEABLE @ cacheable
or r7, r7, #PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC
/*
* Create identity mapping for first 4MB of kernel to
* cater for the MMU enable. This identity mapping
* will be removed by paging_init(). We use our current program
* counter to determine corresponding section base address.
*/
mov r6, pc
mov r6, r6 >> #22 @ start of kernel section
or r1, r7, r6 << #22 @ flags + kernel base
stw r1, [r4+], r6 << #2 @ identity mapping
/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
add r0, r4, #(KERNEL_START & 0xff000000) >> 20
stw.w r1, [r0+], #(KERNEL_START & 0x00c00000) >> 20
movl r6, #(KERNEL_END - 1)
add r0, r0, #4
add r6, r4, r6 >> #20
102: csub.a r0, r6
add r1, r1, #1 << 22
bua 103f
stw.w r1, [r0]+, #4
b 102b
103:
/*
* Then map first 4MB of ram in case it contains our boot params.
*/
add r0, r4, #PAGE_OFFSET >> 20
or r6, r7, #(PHYS_OFFSET & 0xffc00000)
stw r6, [r0]
ldw r15, __switch_data @ address to jump to after
/*
* Initialise TLB, Caches, and MMU state ready to switch the MMU
* on.
*/
mov r0, #0
movc p0.c5, r0, #28 @ cache invalidate all
nop8
movc p0.c6, r0, #6 @ TLB invalidate all
nop8
/*
* ..V. .... ..TB IDAM
* ..1. .... ..01 1111
*/
movl r0, #0x201f @ control register setting
/*
* Setup common bits before finally enabling the MMU. Essentially
* this is just loading the page table pointer and domain access
* registers.
*/
#ifndef CONFIG_ALIGNMENT_TRAP
andn r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
andn r0, r0, #CR_D
#endif
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
andn r0, r0, #CR_B
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
andn r0, r0, #CR_I
#endif
movc p0.c2, r4, #0 @ set pgd
b __turn_mmu_on
ENDPROC(stext)
/*
* Enable the MMU. This completely changes the structure of the visible
* memory space. You will not be able to trace execution through this.
*
* r0 = cp#0 control register
* r15 = *virtual* address to jump to upon completion
*/
.align 5
__turn_mmu_on:
mov r0, r0
movc p0.c1, r0, #0 @ write control reg
nop @ fetch inst by phys addr
mov pc, r15
nop8 @ fetch inst by phys addr
ENDPROC(__turn_mmu_on)
/*
* Setup the initial page tables. We only setup the barest
* amount which are required to get the kernel running, which
* generally means mapping in the kernel code.
*
* r9 = cpuid
* r10 = procinfo
*
* Returns:
* r0, r3, r6, r7 corrupted
* r4 = physical page table address
*/
.ltorg
.align 2
.type __switch_data, %object
__switch_data:
.long __mmap_switched
.long __bss_start @ r6
.long _end @ r7
.long cr_alignment @ r8
.long init_thread_union + THREAD_START_SP @ sp
/*
* The following fragment of code is executed with the MMU on in MMU mode,
* and uses absolute addresses; this is not position independent.
*
* r0 = cp#0 control register
*/
__mmap_switched:
adr r3, __switch_data + 4
ldm.w (r6, r7, r8), [r3]+
ldw sp, [r3]
mov fp, #0 @ Clear BSS (and zero fp)
203: csub.a r6, r7
bea 204f
stw.w fp, [r6]+,#4
b 203b
204:
andn r1, r0, #CR_A @ Clear 'A' bit
stm (r0, r1), [r8]+ @ Save control register values
b start_kernel
ENDPROC(__mmap_switched)
/*
* Exception handling. Something went wrong and we can't proceed. We
* ought to tell the user, but since we don't have any guarantee that
* we're even running on the right architecture, we do virtually nothing.
*
* If CONFIG_DEBUG_LL is set we try to print out something about the error
* and hope for the best (useful if bootloader fails to pass a proper
* machine ID for example).
*/
__error_p:
#ifdef CONFIG_DEBUG_LL
adr r0, str_p1
b.l printascii
mov r0, r9
b.l printhex8
adr r0, str_p2
b.l printascii
901: nop8
b 901b
str_p1: .asciz "\nError: unrecognized processor variant (0x"
str_p2: .asciz ").\n"
.align
#endif
ENDPROC(__error_p)

View File

@@ -0,0 +1,159 @@
/*
* linux/arch/unicore32/kernel/hibernate.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/gfp.h>
#include <linux/suspend.h>
#include <linux/bootmem.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/suspend.h>
#include "mach/pm.h"
/* Pointer to the temporary resume page tables */
pgd_t *resume_pg_dir;
struct swsusp_arch_regs swsusp_arch_regs_cpu0;
/*
* Create a middle page table on a resume-safe page and put a pointer to it in
* the given global directory entry. This only returns the gd entry
* in non-PAE compilation mode, since the middle layer is folded.
*/
static pmd_t *resume_one_md_table_init(pgd_t *pgd)
{
pud_t *pud;
pmd_t *pmd_table;
pud = pud_offset(pgd, 0);
pmd_table = pmd_offset(pud, 0);
return pmd_table;
}
/*
* Create a page table on a resume-safe page and place a pointer to it in
* a middle page directory entry.
*/
static pte_t *resume_one_page_table_init(pmd_t *pmd)
{
if (pmd_none(*pmd)) {
pte_t *page_table = (pte_t *)get_safe_page(GFP_ATOMIC);
if (!page_table)
return NULL;
set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_KERNEL_TABLE));
BUG_ON(page_table != pte_offset_kernel(pmd, 0));
return page_table;
}
return pte_offset_kernel(pmd, 0);
}
/*
* This maps the physical memory to kernel virtual address space, a total
* of max_low_pfn pages, by creating page tables starting from address
* PAGE_OFFSET. The page tables are allocated out of resume-safe pages.
*/
static int resume_physical_mapping_init(pgd_t *pgd_base)
{
unsigned long pfn;
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
int pgd_idx, pmd_idx;
pgd_idx = pgd_index(PAGE_OFFSET);
pgd = pgd_base + pgd_idx;
pfn = 0;
for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
pmd = resume_one_md_table_init(pgd);
if (!pmd)
return -ENOMEM;
if (pfn >= max_low_pfn)
continue;
for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD; pmd++, pmd_idx++) {
pte_t *max_pte;
if (pfn >= max_low_pfn)
break;
/* Map with normal page tables.
* NOTE: We can mark everything as executable here
*/
pte = resume_one_page_table_init(pmd);
if (!pte)
return -ENOMEM;
max_pte = pte + PTRS_PER_PTE;
for (; pte < max_pte; pte++, pfn++) {
if (pfn >= max_low_pfn)
break;
set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
}
}
}
return 0;
}
static inline void resume_init_first_level_page_table(pgd_t *pg_dir)
{
}
int swsusp_arch_resume(void)
{
int error;
resume_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC);
if (!resume_pg_dir)
return -ENOMEM;
resume_init_first_level_page_table(resume_pg_dir);
error = resume_physical_mapping_init(resume_pg_dir);
if (error)
return error;
/* We have got enough memory and from now on we cannot recover */
restore_image(resume_pg_dir, restore_pblist);
return 0;
}
/*
* pfn_is_nosave - check if given pfn is in the 'nosave' section
*/
int pfn_is_nosave(unsigned long pfn)
{
unsigned long begin_pfn = __pa(&__nosave_begin) >> PAGE_SHIFT;
unsigned long end_pfn = PAGE_ALIGN(__pa(&__nosave_end)) >> PAGE_SHIFT;
return (pfn >= begin_pfn) && (pfn < end_pfn);
}
void save_processor_state(void)
{
}
void restore_processor_state(void)
{
local_flush_tlb_all();
}

View File

@@ -0,0 +1,117 @@
/*
* linux/arch/unicore32/kernel/hibernate_asm.S
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/sys.h>
#include <linux/errno.h>
#include <linux/linkage.h>
#include <generated/asm-offsets.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/assembler.h>
@ restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist)
@ r0: resume_pg_dir
@ r1: restore_pblist
@ copy restore_pblist pages
@ restore registers from swsusp_arch_regs_cpu0
@
ENTRY(restore_image)
sub r0, r0, #PAGE_OFFSET
mov r5, #0
movc p0.c6, r5, #6 @invalidate ITLB & DTLB
movc p0.c2, r0, #0
nop
nop
nop
nop
nop
nop
nop
.p2align 4,,7
101:
csub.a r1, #0
beq 109f
ldw r6, [r1+], #PBE_ADDRESS
ldw r7, [r1+], #PBE_ORIN_ADDRESS
movl ip, #128
102: ldm.w (r8 - r15), [r6]+
stm.w (r8 - r15), [r7]+
sub.a ip, ip, #1
bne 102b
ldw r1, [r1+], #PBE_NEXT
b 101b
.p2align 4,,7
109:
/* go back to the original page tables */
ldw r0, =swapper_pg_dir
sub r0, r0, #PAGE_OFFSET
mov r5, #0
movc p0.c6, r5, #6
movc p0.c2, r0, #0
nop
nop
nop
nop
nop
nop
nop
#ifdef CONFIG_UNICORE_FPU_F64
ldw ip, 1f
add ip, ip, #SWSUSP_FPSTATE
lfm.w (f0 - f7 ), [ip]+
lfm.w (f8 - f15), [ip]+
lfm.w (f16 - f23), [ip]+
lfm.w (f24 - f31), [ip]+
ldw r4, [ip]
ctf r4, s31
#endif
mov r0, #0x0
ldw ip, 1f
add ip, ip, #SWSUSP_CPU
ldm.w (r4 - r15), [ip]+
ldm (r16 - r27, sp, pc), [ip]+ @ Load all regs saved previously
.align 2
1: .long swsusp_arch_regs_cpu0
@ swsusp_arch_suspend()
@ - prepare pc for resume, return from function without swsusp_save on resume
@ - save registers in swsusp_arch_regs_cpu0
@ - call swsusp_save write suspend image
ENTRY(swsusp_arch_suspend)
ldw ip, 1f
add ip, ip, #SWSUSP_CPU
stm.w (r4 - r15), [ip]+
stm.w (r16 - r27, sp, lr), [ip]+
#ifdef CONFIG_UNICORE_FPU_F64
ldw ip, 1f
add ip, ip, #SWSUSP_FPSTATE
sfm.w (f0 - f7 ), [ip]+
sfm.w (f8 - f15), [ip]+
sfm.w (f16 - f23), [ip]+
sfm.w (f24 - f31), [ip]+
cff r4, s31
stw r4, [ip]
#endif
b swsusp_save @ no return
1: .long swsusp_arch_regs_cpu0

376
arch/unicore32/kernel/irq.c Normal file
View File

@@ -0,0 +1,376 @@
/*
* linux/arch/unicore32/kernel/irq.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/random.h>
#include <linux/smp.h>
#include <linux/init.h>
#include <linux/seq_file.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/kallsyms.h>
#include <linux/proc_fs.h>
#include <linux/syscore_ops.h>
#include <linux/gpio.h>
#include <mach/hardware.h>
#include "setup.h"
/*
* PKUnity GPIO edge detection for IRQs:
* IRQs are generated on Falling-Edge, Rising-Edge, or both.
* Use this instead of directly setting GRER/GFER.
*/
static int GPIO_IRQ_rising_edge;
static int GPIO_IRQ_falling_edge;
static int GPIO_IRQ_mask = 0;
#define GPIO_MASK(irq) (1 << (irq - IRQ_GPIO0))
static int puv3_gpio_type(struct irq_data *d, unsigned int type)
{
unsigned int mask;
if (d->irq < IRQ_GPIOHIGH)
mask = 1 << d->irq;
else
mask = GPIO_MASK(d->irq);
if (type == IRQ_TYPE_PROBE) {
if ((GPIO_IRQ_rising_edge | GPIO_IRQ_falling_edge) & mask)
return 0;
type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
}
if (type & IRQ_TYPE_EDGE_RISING)
GPIO_IRQ_rising_edge |= mask;
else
GPIO_IRQ_rising_edge &= ~mask;
if (type & IRQ_TYPE_EDGE_FALLING)
GPIO_IRQ_falling_edge |= mask;
else
GPIO_IRQ_falling_edge &= ~mask;
writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER);
writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER);
return 0;
}
/*
* GPIO IRQs must be acknowledged. This is for IRQs from 0 to 7.
*/
static void puv3_low_gpio_ack(struct irq_data *d)
{
writel((1 << d->irq), GPIO_GEDR);
}
static void puv3_low_gpio_mask(struct irq_data *d)
{
writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR);
}
static void puv3_low_gpio_unmask(struct irq_data *d)
{
writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR);
}
static int puv3_low_gpio_wake(struct irq_data *d, unsigned int on)
{
if (on)
writel(readl(PM_PWER) | (1 << d->irq), PM_PWER);
else
writel(readl(PM_PWER) & ~(1 << d->irq), PM_PWER);
return 0;
}
static struct irq_chip puv3_low_gpio_chip = {
.name = "GPIO-low",
.irq_ack = puv3_low_gpio_ack,
.irq_mask = puv3_low_gpio_mask,
.irq_unmask = puv3_low_gpio_unmask,
.irq_set_type = puv3_gpio_type,
.irq_set_wake = puv3_low_gpio_wake,
};
/*
* IRQ8 (GPIO0 through 27) handler. We enter here with the
* irq_controller_lock held, and IRQs disabled. Decode the IRQ
* and call the handler.
*/
static void
puv3_gpio_handler(unsigned int irq, struct irq_desc *desc)
{
unsigned int mask;
mask = readl(GPIO_GEDR);
do {
/*
* clear down all currently active IRQ sources.
* We will be processing them all.
*/
writel(mask, GPIO_GEDR);
irq = IRQ_GPIO0;
do {
if (mask & 1)
generic_handle_irq(irq);
mask >>= 1;
irq++;
} while (mask);
mask = readl(GPIO_GEDR);
} while (mask);
}
/*
* GPIO0-27 edge IRQs need to be handled specially.
* In addition, the IRQs are all collected up into one bit in the
* interrupt controller registers.
*/
static void puv3_high_gpio_ack(struct irq_data *d)
{
unsigned int mask = GPIO_MASK(d->irq);
writel(mask, GPIO_GEDR);
}
static void puv3_high_gpio_mask(struct irq_data *d)
{
unsigned int mask = GPIO_MASK(d->irq);
GPIO_IRQ_mask &= ~mask;
writel(readl(GPIO_GRER) & ~mask, GPIO_GRER);
writel(readl(GPIO_GFER) & ~mask, GPIO_GFER);
}
static void puv3_high_gpio_unmask(struct irq_data *d)
{
unsigned int mask = GPIO_MASK(d->irq);
GPIO_IRQ_mask |= mask;
writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER);
writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER);
}
static int puv3_high_gpio_wake(struct irq_data *d, unsigned int on)
{
if (on)
writel(readl(PM_PWER) | PM_PWER_GPIOHIGH, PM_PWER);
else
writel(readl(PM_PWER) & ~PM_PWER_GPIOHIGH, PM_PWER);
return 0;
}
static struct irq_chip puv3_high_gpio_chip = {
.name = "GPIO-high",
.irq_ack = puv3_high_gpio_ack,
.irq_mask = puv3_high_gpio_mask,
.irq_unmask = puv3_high_gpio_unmask,
.irq_set_type = puv3_gpio_type,
.irq_set_wake = puv3_high_gpio_wake,
};
/*
* We don't need to ACK IRQs on the PKUnity unless they're GPIOs
* this is for internal IRQs i.e. from 8 to 31.
*/
static void puv3_mask_irq(struct irq_data *d)
{
writel(readl(INTC_ICMR) & ~(1 << d->irq), INTC_ICMR);
}
static void puv3_unmask_irq(struct irq_data *d)
{
writel(readl(INTC_ICMR) | (1 << d->irq), INTC_ICMR);
}
/*
* Apart form GPIOs, only the RTC alarm can be a wakeup event.
*/
static int puv3_set_wake(struct irq_data *d, unsigned int on)
{
if (d->irq == IRQ_RTCAlarm) {
if (on)
writel(readl(PM_PWER) | PM_PWER_RTC, PM_PWER);
else
writel(readl(PM_PWER) & ~PM_PWER_RTC, PM_PWER);
return 0;
}
return -EINVAL;
}
static struct irq_chip puv3_normal_chip = {
.name = "PKUnity-v3",
.irq_ack = puv3_mask_irq,
.irq_mask = puv3_mask_irq,
.irq_unmask = puv3_unmask_irq,
.irq_set_wake = puv3_set_wake,
};
static struct resource irq_resource = {
.name = "irqs",
.start = io_v2p(PKUNITY_INTC_BASE),
.end = io_v2p(PKUNITY_INTC_BASE) + 0xFFFFF,
};
static struct puv3_irq_state {
unsigned int saved;
unsigned int icmr;
unsigned int iclr;
unsigned int iccr;
} puv3_irq_state;
static int puv3_irq_suspend(void)
{
struct puv3_irq_state *st = &puv3_irq_state;
st->saved = 1;
st->icmr = readl(INTC_ICMR);
st->iclr = readl(INTC_ICLR);
st->iccr = readl(INTC_ICCR);
/*
* Disable all GPIO-based interrupts.
*/
writel(readl(INTC_ICMR) & ~(0x1ff), INTC_ICMR);
/*
* Set the appropriate edges for wakeup.
*/
writel(readl(PM_PWER) & GPIO_IRQ_rising_edge, GPIO_GRER);
writel(readl(PM_PWER) & GPIO_IRQ_falling_edge, GPIO_GFER);
/*
* Clear any pending GPIO interrupts.
*/
writel(readl(GPIO_GEDR), GPIO_GEDR);
return 0;
}
static void puv3_irq_resume(void)
{
struct puv3_irq_state *st = &puv3_irq_state;
if (st->saved) {
writel(st->iccr, INTC_ICCR);
writel(st->iclr, INTC_ICLR);
writel(GPIO_IRQ_rising_edge & GPIO_IRQ_mask, GPIO_GRER);
writel(GPIO_IRQ_falling_edge & GPIO_IRQ_mask, GPIO_GFER);
writel(st->icmr, INTC_ICMR);
}
}
static struct syscore_ops puv3_irq_syscore_ops = {
.suspend = puv3_irq_suspend,
.resume = puv3_irq_resume,
};
static int __init puv3_irq_init_syscore(void)
{
register_syscore_ops(&puv3_irq_syscore_ops);
return 0;
}
device_initcall(puv3_irq_init_syscore);
void __init init_IRQ(void)
{
unsigned int irq;
request_resource(&iomem_resource, &irq_resource);
/* disable all IRQs */
writel(0, INTC_ICMR);
/* all IRQs are IRQ, not REAL */
writel(0, INTC_ICLR);
/* clear all GPIO edge detects */
writel(FMASK(8, 0) & ~FIELD(1, 1, GPI_SOFF_REQ), GPIO_GPIR);
writel(0, GPIO_GFER);
writel(0, GPIO_GRER);
writel(0x0FFFFFFF, GPIO_GEDR);
writel(1, INTC_ICCR);
for (irq = 0; irq < IRQ_GPIOHIGH; irq++) {
irq_set_chip(irq, &puv3_low_gpio_chip);
irq_set_handler(irq, handle_edge_irq);
irq_modify_status(irq,
IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN,
0);
}
for (irq = IRQ_GPIOHIGH + 1; irq < IRQ_GPIO0; irq++) {
irq_set_chip(irq, &puv3_normal_chip);
irq_set_handler(irq, handle_level_irq);
irq_modify_status(irq,
IRQ_NOREQUEST | IRQ_NOAUTOEN,
IRQ_NOPROBE);
}
for (irq = IRQ_GPIO0; irq <= IRQ_GPIO27; irq++) {
irq_set_chip(irq, &puv3_high_gpio_chip);
irq_set_handler(irq, handle_edge_irq);
irq_modify_status(irq,
IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN,
0);
}
/*
* Install handler for GPIO 0-27 edge detect interrupts
*/
irq_set_chip(IRQ_GPIOHIGH, &puv3_normal_chip);
irq_set_chained_handler(IRQ_GPIOHIGH, puv3_gpio_handler);
#ifdef CONFIG_PUV3_GPIO
puv3_init_gpio();
#endif
}
/*
* do_IRQ handles all hardware IRQ's. Decoded IRQs should not
* come via this function. Instead, they should provide their
* own 'handler'
*/
asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
irq_enter();
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if (unlikely(irq >= nr_irqs)) {
if (printk_ratelimit())
printk(KERN_WARNING "Bad IRQ%u\n", irq);
ack_bad_irq(irq);
} else {
generic_handle_irq(irq);
}
irq_exit();
set_irq_regs(old_regs);
}

View File

@@ -0,0 +1,98 @@
/*
* linux/arch/unicore32/kernel/ksyms.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/cryptohash.h>
#include <linux/delay.h>
#include <linux/in6.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <asm/checksum.h>
#include "ksyms.h"
EXPORT_SYMBOL(find_next_zero_bit);
EXPORT_SYMBOL(find_next_bit);
EXPORT_SYMBOL(__backtrace);
/* platform dependent support */
EXPORT_SYMBOL(__udelay);
EXPORT_SYMBOL(__const_udelay);
/* networking */
EXPORT_SYMBOL(csum_partial);
EXPORT_SYMBOL(csum_partial_copy_from_user);
EXPORT_SYMBOL(csum_partial_copy_nocheck);
EXPORT_SYMBOL(__csum_ipv6_magic);
/* io */
#ifndef __raw_readsb
EXPORT_SYMBOL(__raw_readsb);
#endif
#ifndef __raw_readsw
EXPORT_SYMBOL(__raw_readsw);
#endif
#ifndef __raw_readsl
EXPORT_SYMBOL(__raw_readsl);
#endif
#ifndef __raw_writesb
EXPORT_SYMBOL(__raw_writesb);
#endif
#ifndef __raw_writesw
EXPORT_SYMBOL(__raw_writesw);
#endif
#ifndef __raw_writesl
EXPORT_SYMBOL(__raw_writesl);
#endif
/* string / mem functions */
EXPORT_SYMBOL(strchr);
EXPORT_SYMBOL(strrchr);
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(memchr);
/* user mem (segment) */
EXPORT_SYMBOL(__strnlen_user);
EXPORT_SYMBOL(__strncpy_from_user);
EXPORT_SYMBOL(copy_page);
EXPORT_SYMBOL(__copy_from_user);
EXPORT_SYMBOL(__copy_to_user);
EXPORT_SYMBOL(__clear_user);
EXPORT_SYMBOL(__get_user_1);
EXPORT_SYMBOL(__get_user_2);
EXPORT_SYMBOL(__get_user_4);
EXPORT_SYMBOL(__put_user_1);
EXPORT_SYMBOL(__put_user_2);
EXPORT_SYMBOL(__put_user_4);
EXPORT_SYMBOL(__put_user_8);
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__ashrdi3);
EXPORT_SYMBOL(__divsi3);
EXPORT_SYMBOL(__lshrdi3);
EXPORT_SYMBOL(__modsi3);
EXPORT_SYMBOL(__muldi3);
EXPORT_SYMBOL(__ucmpdi2);
EXPORT_SYMBOL(__udivsi3);
EXPORT_SYMBOL(__umodsi3);
EXPORT_SYMBOL(__bswapsi2);

View File

@@ -0,0 +1,15 @@
/*
* libgcc functions - functions that are used internally by the
* compiler... (prototypes are not correct though, but that
* doesn't really matter since they're not versioned).
*/
extern void __ashldi3(void);
extern void __ashrdi3(void);
extern void __divsi3(void);
extern void __lshrdi3(void);
extern void __modsi3(void);
extern void __muldi3(void);
extern void __ucmpdi2(void);
extern void __udivsi3(void);
extern void __umodsi3(void);
extern void __bswapsi2(void);

View File

@@ -0,0 +1,114 @@
/*
* linux/arch/unicore32/kernel/module.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleloader.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/elf.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/gfp.h>
#include <asm/pgtable.h>
#include <asm/sections.h>
void *module_alloc(unsigned long size)
{
struct vm_struct *area;
size = PAGE_ALIGN(size);
area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
if (!area)
return NULL;
return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC);
}
int
apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
unsigned int relindex, struct module *module)
{
Elf32_Shdr *symsec = sechdrs + symindex;
Elf32_Shdr *relsec = sechdrs + relindex;
Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
Elf32_Rel *rel = (void *)relsec->sh_addr;
unsigned int i;
for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) {
unsigned long loc;
Elf32_Sym *sym;
s32 offset;
offset = ELF32_R_SYM(rel->r_info);
if (offset < 0 || offset >
(symsec->sh_size / sizeof(Elf32_Sym))) {
printk(KERN_ERR "%s: bad relocation, "
"section %d reloc %d\n",
module->name, relindex, i);
return -ENOEXEC;
}
sym = ((Elf32_Sym *)symsec->sh_addr) + offset;
if (rel->r_offset < 0 || rel->r_offset >
dstsec->sh_size - sizeof(u32)) {
printk(KERN_ERR "%s: out of bounds relocation, "
"section %d reloc %d offset %d size %d\n",
module->name, relindex, i, rel->r_offset,
dstsec->sh_size);
return -ENOEXEC;
}
loc = dstsec->sh_addr + rel->r_offset;
switch (ELF32_R_TYPE(rel->r_info)) {
case R_UNICORE_NONE:
/* ignore */
break;
case R_UNICORE_ABS32:
*(u32 *)loc += sym->st_value;
break;
case R_UNICORE_PC24:
case R_UNICORE_CALL:
case R_UNICORE_JUMP24:
offset = (*(u32 *)loc & 0x00ffffff) << 2;
if (offset & 0x02000000)
offset -= 0x04000000;
offset += sym->st_value - loc;
if (offset & 3 ||
offset <= (s32)0xfe000000 ||
offset >= (s32)0x02000000) {
printk(KERN_ERR
"%s: relocation out of range, section "
"%d reloc %d sym '%s'\n", module->name,
relindex, i, strtab + sym->st_name);
return -ENOEXEC;
}
offset >>= 2;
*(u32 *)loc &= 0xff000000;
*(u32 *)loc |= offset & 0x00ffffff;
break;
default:
printk(KERN_ERR "%s: unknown relocation: %u\n",
module->name, ELF32_R_TYPE(rel->r_info));
return -ENOEXEC;
}
}
return 0;
}

398
arch/unicore32/kernel/pci.c Normal file
View File

@@ -0,0 +1,398 @@
/*
* linux/arch/unicore32/kernel/pci.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* PCI bios-type initialisation for PCI machines
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/io.h>
static int debug_pci;
#define CONFIG_CMD(bus, devfn, where) \
(0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
static int
puv3_read_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 *value)
{
writel(CONFIG_CMD(bus, devfn, where), PCICFG_ADDR);
switch (size) {
case 1:
*value = (readl(PCICFG_DATA) >> ((where & 3) * 8)) & 0xFF;
break;
case 2:
*value = (readl(PCICFG_DATA) >> ((where & 2) * 8)) & 0xFFFF;
break;
case 4:
*value = readl(PCICFG_DATA);
break;
}
return PCIBIOS_SUCCESSFUL;
}
static int
puv3_write_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 value)
{
writel(CONFIG_CMD(bus, devfn, where), PCICFG_ADDR);
switch (size) {
case 1:
writel((readl(PCICFG_DATA) & ~FMASK(8, (where&3)*8))
| FIELD(value, 8, (where&3)*8), PCICFG_DATA);
break;
case 2:
writel((readl(PCICFG_DATA) & ~FMASK(16, (where&2)*8))
| FIELD(value, 16, (where&2)*8), PCICFG_DATA);
break;
case 4:
writel(value, PCICFG_DATA);
break;
}
return PCIBIOS_SUCCESSFUL;
}
struct pci_ops pci_puv3_ops = {
.read = puv3_read_config,
.write = puv3_write_config,
};
void pci_puv3_preinit(void)
{
printk(KERN_DEBUG "PCI: PKUnity PCI Controller Initializing ...\n");
/* config PCI bridge base */
writel(io_v2p(PKUNITY_PCIBRI_BASE), PCICFG_BRIBASE);
writel(0, PCIBRI_AHBCTL0);
writel(io_v2p(PKUNITY_PCIBRI_BASE) | PCIBRI_BARx_MEM, PCIBRI_AHBBAR0);
writel(0xFFFF0000, PCIBRI_AHBAMR0);
writel(0, PCIBRI_AHBTAR0);
writel(PCIBRI_CTLx_AT, PCIBRI_AHBCTL1);
writel(io_v2p(PKUNITY_PCILIO_BASE) | PCIBRI_BARx_IO, PCIBRI_AHBBAR1);
writel(0xFFFF0000, PCIBRI_AHBAMR1);
writel(0x00000000, PCIBRI_AHBTAR1);
writel(PCIBRI_CTLx_PREF, PCIBRI_AHBCTL2);
writel(io_v2p(PKUNITY_PCIMEM_BASE) | PCIBRI_BARx_MEM, PCIBRI_AHBBAR2);
writel(0xF8000000, PCIBRI_AHBAMR2);
writel(0, PCIBRI_AHBTAR2);
writel(io_v2p(PKUNITY_PCIAHB_BASE) | PCIBRI_BARx_MEM, PCIBRI_BAR1);
writel(PCIBRI_CTLx_AT | PCIBRI_CTLx_PREF, PCIBRI_PCICTL0);
writel(io_v2p(PKUNITY_PCIAHB_BASE) | PCIBRI_BARx_MEM, PCIBRI_PCIBAR0);
writel(0xF8000000, PCIBRI_PCIAMR0);
writel(PKUNITY_SDRAM_BASE, PCIBRI_PCITAR0);
writel(readl(PCIBRI_CMD) | PCIBRI_CMD_IO | PCIBRI_CMD_MEM, PCIBRI_CMD);
}
static int __init pci_puv3_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (dev->bus->number == 0) {
#ifdef CONFIG_ARCH_FPGA /* 4 pci slots */
if (dev->devfn == 0x00)
return IRQ_PCIINTA;
else if (dev->devfn == 0x08)
return IRQ_PCIINTB;
else if (dev->devfn == 0x10)
return IRQ_PCIINTC;
else if (dev->devfn == 0x18)
return IRQ_PCIINTD;
#endif
#ifdef CONFIG_PUV3_DB0913 /* 3 pci slots */
if (dev->devfn == 0x30)
return IRQ_PCIINTB;
else if (dev->devfn == 0x60)
return IRQ_PCIINTC;
else if (dev->devfn == 0x58)
return IRQ_PCIINTD;
#endif
#if defined(CONFIG_PUV3_NB0916) || defined(CONFIG_PUV3_SMW0919)
/* only support 2 pci devices */
if (dev->devfn == 0x00)
return IRQ_PCIINTC; /* sata */
#endif
}
return -1;
}
/*
* Only first 128MB of memory can be accessed via PCI.
* We use GFP_DMA to allocate safe buffers to do map/unmap.
* This is really ugly and we need a better way of specifying
* DMA-capable regions of memory.
*/
void __init puv3_pci_adjust_zones(unsigned long *zone_size,
unsigned long *zhole_size)
{
unsigned int sz = SZ_128M >> PAGE_SHIFT;
/*
* Only adjust if > 128M on current system
*/
if (zone_size[0] <= sz)
return;
zone_size[1] = zone_size[0] - sz;
zone_size[0] = sz;
zhole_size[1] = zhole_size[0];
zhole_size[0] = 0;
}
/*
* If the bus contains any of these devices, then we must not turn on
* parity checking of any kind.
*/
static inline int pdev_bad_for_parity(struct pci_dev *dev)
{
return 0;
}
/*
* pcibios_fixup_bus - Called after each bus is probed,
* but before its children are examined.
*/
void pcibios_fixup_bus(struct pci_bus *bus)
{
struct pci_dev *dev;
u16 features = PCI_COMMAND_SERR
| PCI_COMMAND_PARITY
| PCI_COMMAND_FAST_BACK;
bus->resource[0] = &ioport_resource;
bus->resource[1] = &iomem_resource;
/*
* Walk the devices on this bus, working out what we can
* and can't support.
*/
list_for_each_entry(dev, &bus->devices, bus_list) {
u16 status;
pci_read_config_word(dev, PCI_STATUS, &status);
/*
* If any device on this bus does not support fast back
* to back transfers, then the bus as a whole is not able
* to support them. Having fast back to back transfers
* on saves us one PCI cycle per transaction.
*/
if (!(status & PCI_STATUS_FAST_BACK))
features &= ~PCI_COMMAND_FAST_BACK;
if (pdev_bad_for_parity(dev))
features &= ~(PCI_COMMAND_SERR
| PCI_COMMAND_PARITY);
switch (dev->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status);
status |= PCI_BRIDGE_CTL_PARITY
| PCI_BRIDGE_CTL_MASTER_ABORT;
status &= ~(PCI_BRIDGE_CTL_BUS_RESET
| PCI_BRIDGE_CTL_FAST_BACK);
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status);
break;
case PCI_CLASS_BRIDGE_CARDBUS:
pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL,
&status);
status |= PCI_CB_BRIDGE_CTL_PARITY
| PCI_CB_BRIDGE_CTL_MASTER_ABORT;
pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL,
status);
break;
}
}
/*
* Now walk the devices again, this time setting them up.
*/
list_for_each_entry(dev, &bus->devices, bus_list) {
u16 cmd;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
cmd |= features;
pci_write_config_word(dev, PCI_COMMAND, cmd);
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
L1_CACHE_BYTES >> 2);
}
/*
* Propagate the flags to the PCI bridge.
*/
if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
if (features & PCI_COMMAND_FAST_BACK)
bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK;
if (features & PCI_COMMAND_PARITY)
bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY;
}
/*
* Report what we did for this bus
*/
printk(KERN_INFO "PCI: bus%d: Fast back to back transfers %sabled\n",
bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis");
}
EXPORT_SYMBOL(pcibios_fixup_bus);
static int __init pci_common_init(void)
{
struct pci_bus *puv3_bus;
pci_puv3_preinit();
puv3_bus = pci_scan_bus(0, &pci_puv3_ops, NULL);
if (!puv3_bus)
panic("PCI: unable to scan bus!");
pci_fixup_irqs(pci_common_swizzle, pci_puv3_map_irq);
if (!pci_has_flag(PCI_PROBE_ONLY)) {
/*
* Size the bridge windows.
*/
pci_bus_size_bridges(puv3_bus);
/*
* Assign resources.
*/
pci_bus_assign_resources(puv3_bus);
}
/*
* Tell drivers about devices found.
*/
pci_bus_add_devices(puv3_bus);
return 0;
}
subsys_initcall(pci_common_init);
char * __init pcibios_setup(char *str)
{
if (!strcmp(str, "debug")) {
debug_pci = 1;
return NULL;
} else if (!strcmp(str, "firmware")) {
pci_add_flags(PCI_PROBE_ONLY);
return NULL;
}
return str;
}
void pcibios_set_master(struct pci_dev *dev)
{
/* No special bus mastering setup handling */
}
/*
* From arch/i386/kernel/pci-i386.c:
*
* We need to avoid collisions with `mirrored' VGA ports
* and other strange ISA hardware, so we always want the
* addresses to be allocated in the 0x000-0x0ff region
* modulo 0x400.
*
* Why? Because some silly external IO cards only decode
* the low 10 bits of the IO address. The 0x00-0xff region
* is reserved for motherboard devices that decode all 16
* bits, so it's ok to allocate at, say, 0x2800-0x28ff,
* but we want to try to avoid allocating at 0x2900-0x2bff
* which might be mirrored at 0x0100-0x03ff..
*/
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
resource_size_t start = res->start;
if (res->flags & IORESOURCE_IO && start & 0x300)
start = (start + 0x3ff) & ~0x3ff;
start = (start + align - 1) & ~(align - 1);
return start;
}
/**
* pcibios_enable_device - Enable I/O and memory.
* @dev: PCI device to be enabled
*/
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
int idx;
struct resource *r;
pci_read_config_word(dev, PCI_COMMAND, &cmd);
old_cmd = cmd;
for (idx = 0; idx < 6; idx++) {
/* Only set up the requested stuff */
if (!(mask & (1 << idx)))
continue;
r = dev->resource + idx;
if (!r->start && r->end) {
printk(KERN_ERR "PCI: Device %s not available because"
" of resource collisions\n", pci_name(dev));
return -EINVAL;
}
if (r->flags & IORESOURCE_IO)
cmd |= PCI_COMMAND_IO;
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
/*
* Bridges (eg, cardbus bridges) need to be fully enabled
*/
if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
if (cmd != old_cmd) {
printk("PCI: enabling device %s (%04x -> %04x)\n",
pci_name(dev), old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
}
return 0;
}
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
enum pci_mmap_state mmap_state, int write_combine)
{
unsigned long phys;
if (mmap_state == pci_mmap_io)
return -EINVAL;
phys = vma->vm_pgoff;
/*
* Mark this as IO
*/
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
if (remap_pfn_range(vma, vma->vm_start, phys,
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
return -EAGAIN;
return 0;
}

123
arch/unicore32/kernel/pm.c Normal file
View File

@@ -0,0 +1,123 @@
/*
* linux/arch/unicore32/kernel/pm.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/suspend.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <mach/hardware.h>
#include <mach/pm.h>
#include "setup.h"
struct puv3_cpu_pm_fns *puv3_cpu_pm_fns;
static unsigned long *sleep_save;
int puv3_pm_enter(suspend_state_t state)
{
unsigned long sleep_save_checksum = 0, checksum = 0;
int i;
/* skip registers saving for standby */
if (state != PM_SUSPEND_STANDBY) {
puv3_cpu_pm_fns->save(sleep_save);
/* before sleeping, calculate and save a checksum */
for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++)
sleep_save_checksum += sleep_save[i];
}
/* *** go zzz *** */
puv3_cpu_pm_fns->enter(state);
cpu_init();
#ifdef CONFIG_INPUT_KEYBOARD
puv3_ps2_init();
#endif
#ifdef CONFIG_PCI
pci_puv3_preinit();
#endif
if (state != PM_SUSPEND_STANDBY) {
/* after sleeping, validate the checksum */
for (i = 0; i < puv3_cpu_pm_fns->save_count - 1; i++)
checksum += sleep_save[i];
/* if invalid, display message and wait for a hardware reset */
if (checksum != sleep_save_checksum) {
while (1)
puv3_cpu_pm_fns->enter(state);
}
puv3_cpu_pm_fns->restore(sleep_save);
}
pr_debug("*** made it back from resume\n");
return 0;
}
EXPORT_SYMBOL_GPL(puv3_pm_enter);
unsigned long sleep_phys_sp(void *sp)
{
return virt_to_phys(sp);
}
static int puv3_pm_valid(suspend_state_t state)
{
if (puv3_cpu_pm_fns)
return puv3_cpu_pm_fns->valid(state);
return -EINVAL;
}
static int puv3_pm_prepare(void)
{
int ret = 0;
if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->prepare)
ret = puv3_cpu_pm_fns->prepare();
return ret;
}
static void puv3_pm_finish(void)
{
if (puv3_cpu_pm_fns && puv3_cpu_pm_fns->finish)
puv3_cpu_pm_fns->finish();
}
static struct platform_suspend_ops puv3_pm_ops = {
.valid = puv3_pm_valid,
.enter = puv3_pm_enter,
.prepare = puv3_pm_prepare,
.finish = puv3_pm_finish,
};
static int __init puv3_pm_init(void)
{
if (!puv3_cpu_pm_fns) {
printk(KERN_ERR "no valid puv3_cpu_pm_fns defined\n");
return -EINVAL;
}
sleep_save = kmalloc(puv3_cpu_pm_fns->save_count
* sizeof(unsigned long), GFP_KERNEL);
if (!sleep_save) {
printk(KERN_ERR "failed to alloc memory for pm save\n");
return -ENOMEM;
}
suspend_set_ops(&puv3_pm_ops);
return 0;
}
device_initcall(puv3_pm_init);

View File

@@ -0,0 +1,337 @@
/*
* linux/arch/unicore32/kernel/process.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <stdarg.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/kallsyms.h>
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/elfcore.h>
#include <linux/pm.h>
#include <linux/tick.h>
#include <linux/utsname.h>
#include <linux/uaccess.h>
#include <linux/random.h>
#include <linux/gpio.h>
#include <linux/stacktrace.h>
#include <asm/cacheflush.h>
#include <asm/processor.h>
#include <asm/stacktrace.h>
#include "setup.h"
static const char * const processor_modes[] = {
"UK00", "UK01", "UK02", "UK03", "UK04", "UK05", "UK06", "UK07",
"UK08", "UK09", "UK0A", "UK0B", "UK0C", "UK0D", "UK0E", "UK0F",
"USER", "REAL", "INTR", "PRIV", "UK14", "UK15", "UK16", "ABRT",
"UK18", "UK19", "UK1A", "EXTN", "UK1C", "UK1D", "UK1E", "SUSR"
};
void arch_cpu_idle(void)
{
cpu_do_idle();
local_irq_enable();
}
static char reboot_mode = 'h';
int __init reboot_setup(char *str)
{
reboot_mode = str[0];
return 1;
}
__setup("reboot=", reboot_setup);
void machine_halt(void)
{
gpio_set_value(GPO_SOFT_OFF, 0);
}
/*
* Function pointers to optional machine specific functions
*/
void (*pm_power_off)(void) = NULL;
void machine_power_off(void)
{
if (pm_power_off)
pm_power_off();
machine_halt();
}
void machine_restart(char *cmd)
{
/* Disable interrupts first */
local_irq_disable();
/*
* Tell the mm system that we are going to reboot -
* we may need it to insert some 1:1 mappings so that
* soft boot works.
*/
setup_mm_for_reboot(reboot_mode);
/* Clean and invalidate caches */
flush_cache_all();
/* Turn off caching */
cpu_proc_fin();
/* Push out any further dirty data, and ensure cache is empty */
flush_cache_all();
/*
* Now handle reboot code.
*/
if (reboot_mode == 's') {
/* Jump into ROM at address 0xffff0000 */
cpu_reset(VECTORS_BASE);
} else {
writel(0x00002001, PM_PLLSYSCFG); /* cpu clk = 250M */
writel(0x00100800, PM_PLLDDRCFG); /* ddr clk = 44M */
writel(0x00002001, PM_PLLVGACFG); /* vga clk = 250M */
/* Use on-chip reset capability */
/* following instructions must be in one icache line */
__asm__ __volatile__(
" .align 5\n\t"
" stw %1, [%0]\n\t"
"201: ldw r0, [%0]\n\t"
" cmpsub.a r0, #0\n\t"
" bne 201b\n\t"
" stw %3, [%2]\n\t"
" nop; nop; nop\n\t"
/* prefetch 3 instructions at most */
:
: "r" (PM_PMCR),
"r" (PM_PMCR_CFBSYS | PM_PMCR_CFBDDR
| PM_PMCR_CFBVGA),
"r" (RESETC_SWRR),
"r" (RESETC_SWRR_SRB)
: "r0", "memory");
}
/*
* Whoops - the architecture was unable to reboot.
* Tell the user!
*/
mdelay(1000);
printk(KERN_EMERG "Reboot failed -- System halted\n");
do { } while (1);
}
void __show_regs(struct pt_regs *regs)
{
unsigned long flags;
char buf[64];
show_regs_print_info(KERN_DEFAULT);
print_symbol("PC is at %s\n", instruction_pointer(regs));
print_symbol("LR is at %s\n", regs->UCreg_lr);
printk(KERN_DEFAULT "pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n"
"sp : %08lx ip : %08lx fp : %08lx\n",
regs->UCreg_pc, regs->UCreg_lr, regs->UCreg_asr,
regs->UCreg_sp, regs->UCreg_ip, regs->UCreg_fp);
printk(KERN_DEFAULT "r26: %08lx r25: %08lx r24: %08lx\n",
regs->UCreg_26, regs->UCreg_25,
regs->UCreg_24);
printk(KERN_DEFAULT "r23: %08lx r22: %08lx r21: %08lx r20: %08lx\n",
regs->UCreg_23, regs->UCreg_22,
regs->UCreg_21, regs->UCreg_20);
printk(KERN_DEFAULT "r19: %08lx r18: %08lx r17: %08lx r16: %08lx\n",
regs->UCreg_19, regs->UCreg_18,
regs->UCreg_17, regs->UCreg_16);
printk(KERN_DEFAULT "r15: %08lx r14: %08lx r13: %08lx r12: %08lx\n",
regs->UCreg_15, regs->UCreg_14,
regs->UCreg_13, regs->UCreg_12);
printk(KERN_DEFAULT "r11: %08lx r10: %08lx r9 : %08lx r8 : %08lx\n",
regs->UCreg_11, regs->UCreg_10,
regs->UCreg_09, regs->UCreg_08);
printk(KERN_DEFAULT "r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
regs->UCreg_07, regs->UCreg_06,
regs->UCreg_05, regs->UCreg_04);
printk(KERN_DEFAULT "r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
regs->UCreg_03, regs->UCreg_02,
regs->UCreg_01, regs->UCreg_00);
flags = regs->UCreg_asr;
buf[0] = flags & PSR_S_BIT ? 'S' : 's';
buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z';
buf[2] = flags & PSR_C_BIT ? 'C' : 'c';
buf[3] = flags & PSR_V_BIT ? 'V' : 'v';
buf[4] = '\0';
printk(KERN_DEFAULT "Flags: %s INTR o%s REAL o%s Mode %s Segment %s\n",
buf, interrupts_enabled(regs) ? "n" : "ff",
fast_interrupts_enabled(regs) ? "n" : "ff",
processor_modes[processor_mode(regs)],
segment_eq(get_fs(), get_ds()) ? "kernel" : "user");
{
unsigned int ctrl;
buf[0] = '\0';
{
unsigned int transbase;
asm("movc %0, p0.c2, #0\n"
: "=r" (transbase));
snprintf(buf, sizeof(buf), " Table: %08x", transbase);
}
asm("movc %0, p0.c1, #0\n" : "=r" (ctrl));
printk(KERN_DEFAULT "Control: %08x%s\n", ctrl, buf);
}
}
void show_regs(struct pt_regs *regs)
{
printk(KERN_DEFAULT "\n");
printk(KERN_DEFAULT "Pid: %d, comm: %20s\n",
task_pid_nr(current), current->comm);
__show_regs(regs);
__backtrace();
}
/*
* Free current thread data structures etc..
*/
void exit_thread(void)
{
}
void flush_thread(void)
{
struct thread_info *thread = current_thread_info();
struct task_struct *tsk = current;
memset(thread->used_cp, 0, sizeof(thread->used_cp));
memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
#ifdef CONFIG_UNICORE_FPU_F64
memset(&thread->fpstate, 0, sizeof(struct fp_state));
#endif
}
void release_thread(struct task_struct *dead_task)
{
}
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread");
int
copy_thread(unsigned long clone_flags, unsigned long stack_start,
unsigned long stk_sz, struct task_struct *p)
{
struct thread_info *thread = task_thread_info(p);
struct pt_regs *childregs = task_pt_regs(p);
memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
thread->cpu_context.sp = (unsigned long)childregs;
if (unlikely(p->flags & PF_KTHREAD)) {
thread->cpu_context.pc = (unsigned long)ret_from_kernel_thread;
thread->cpu_context.r4 = stack_start;
thread->cpu_context.r5 = stk_sz;
memset(childregs, 0, sizeof(struct pt_regs));
} else {
thread->cpu_context.pc = (unsigned long)ret_from_fork;
*childregs = *current_pt_regs();
childregs->UCreg_00 = 0;
if (stack_start)
childregs->UCreg_sp = stack_start;
if (clone_flags & CLONE_SETTLS)
childregs->UCreg_16 = childregs->UCreg_03;
}
return 0;
}
/*
* Fill in the task's elfregs structure for a core dump.
*/
int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs)
{
elf_core_copy_regs(elfregs, task_pt_regs(t));
return 1;
}
/*
* fill in the fpe structure for a core dump...
*/
int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fp)
{
struct thread_info *thread = current_thread_info();
int used_math = thread->used_cp[1] | thread->used_cp[2];
#ifdef CONFIG_UNICORE_FPU_F64
if (used_math)
memcpy(fp, &thread->fpstate, sizeof(*fp));
#endif
return used_math != 0;
}
EXPORT_SYMBOL(dump_fpu);
unsigned long get_wchan(struct task_struct *p)
{
struct stackframe frame;
int count = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
frame.fp = thread_saved_fp(p);
frame.sp = thread_saved_sp(p);
frame.lr = 0; /* recovered from the stack */
frame.pc = thread_saved_pc(p);
do {
int ret = unwind_frame(&frame);
if (ret < 0)
return 0;
if (!in_sched_functions(frame.pc))
return frame.pc;
} while ((count++) < 16);
return 0;
}
unsigned long arch_randomize_brk(struct mm_struct *mm)
{
unsigned long range_end = mm->brk + 0x02000000;
return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
}
/*
* The vectors page is always readable from user space for the
* atomic helpers and the signal restart code. Let's declare a mapping
* for it so it is visible through ptrace and /proc/<pid>/mem.
*/
int vectors_user_mapping(void)
{
struct mm_struct *mm = current->mm;
return install_special_mapping(mm, 0xffff0000, PAGE_SIZE,
VM_READ | VM_EXEC |
VM_MAYREAD | VM_MAYEXEC |
VM_DONTEXPAND | VM_DONTDUMP,
NULL);
}
const char *arch_vma_name(struct vm_area_struct *vma)
{
return (vma->vm_start == 0xffff0000) ? "[vectors]" : NULL;
}

View File

@@ -0,0 +1,149 @@
/*
* linux/arch/unicore32/kernel/ptrace.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* By Ross Biro 1/23/92
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/uaccess.h>
/*
* this routine will get a word off of the processes privileged stack.
* the offset is how far from the base addr as stored in the THREAD.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline long get_user_reg(struct task_struct *task, int offset)
{
return task_pt_regs(task)->uregs[offset];
}
/*
* this routine will put a word on the processes privileged stack.
* the offset is how far from the base addr as stored in the THREAD.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline int
put_user_reg(struct task_struct *task, int offset, long data)
{
struct pt_regs newregs, *regs = task_pt_regs(task);
int ret = -EINVAL;
newregs = *regs;
newregs.uregs[offset] = data;
if (valid_user_regs(&newregs)) {
regs->uregs[offset] = data;
ret = 0;
}
return ret;
}
/*
* Called by kernel/ptrace.c when detaching..
*/
void ptrace_disable(struct task_struct *child)
{
}
/*
* We actually access the pt_regs stored on the kernel stack.
*/
static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
unsigned long __user *ret)
{
unsigned long tmp;
tmp = 0;
if (off < sizeof(struct pt_regs))
tmp = get_user_reg(tsk, off >> 2);
return put_user(tmp, ret);
}
/*
* We actually access the pt_regs stored on the kernel stack.
*/
static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
unsigned long val)
{
if (off >= sizeof(struct pt_regs))
return 0;
return put_user_reg(tsk, off >> 2, val);
}
long arch_ptrace(struct task_struct *child, long request,
unsigned long addr, unsigned long data)
{
int ret;
unsigned long __user *datap = (unsigned long __user *) data;
switch (request) {
case PTRACE_PEEKUSR:
ret = ptrace_read_user(child, addr, datap);
break;
case PTRACE_POKEUSR:
ret = ptrace_write_user(child, addr, data);
break;
case PTRACE_GET_THREAD_AREA:
ret = put_user(task_pt_regs(child)->UCreg_16,
datap);
break;
default:
ret = ptrace_request(child, request, addr, data);
break;
}
return ret;
}
asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
{
unsigned long ip;
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return scno;
if (!(current->ptrace & PT_PTRACED))
return scno;
/*
* Save IP. IP is used to denote syscall entry/exit:
* IP = 0 -> entry, = 1 -> exit
*/
ip = regs->UCreg_ip;
regs->UCreg_ip = why;
current_thread_info()->syscall = scno;
/* the 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
? 0x80 : 0));
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code) {
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
regs->UCreg_ip = ip;
return current_thread_info()->syscall;
}

View File

@@ -0,0 +1,279 @@
/*
* linux/arch/unicore32/kernel/puv3-core.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/amba/bus.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/cnt32_to_63.h>
#include <linux/usb/musb.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/pm.h>
/*
* This is the PKUnity sched_clock implementation. This has
* a resolution of 271ns, and a maximum value of 32025597s (370 days).
*
* The return value is guaranteed to be monotonic in that range as
* long as there is always less than 582 seconds between successive
* calls to this function.
*
* ( * 1E9 / CLOCK_TICK_RATE ) -> about 2235/32
*/
unsigned long long sched_clock(void)
{
unsigned long long v = cnt32_to_63(readl(OST_OSCR));
/* original conservative method, but overflow frequently
* v *= NSEC_PER_SEC >> 12;
* do_div(v, CLOCK_TICK_RATE >> 12);
*/
v = ((v & 0x7fffffffffffffffULL) * 2235) >> 5;
return v;
}
static struct resource puv3_usb_resources[] = {
/* order is significant! */
{
.start = io_v2p(PKUNITY_USB_BASE),
.end = io_v2p(PKUNITY_USB_BASE) + 0x3ff,
.flags = IORESOURCE_MEM,
}, {
.start = IRQ_USB,
.flags = IORESOURCE_IRQ,
}, {
.start = IRQ_USB,
.flags = IORESOURCE_IRQ,
},
};
static struct musb_hdrc_config puv3_usb_config[] = {
{
.num_eps = 16,
.multipoint = 1,
#ifdef CONFIG_USB_INVENTRA_DMA
.dma = 1,
.dma_channels = 8,
#endif
},
};
static struct musb_hdrc_platform_data puv3_usb_plat = {
.mode = MUSB_HOST,
.min_power = 100,
.clock = 0,
.config = puv3_usb_config,
};
static struct resource puv3_mmc_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_SDC_BASE),
.end = io_v2p(PKUNITY_SDC_BASE) + 0xfff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDC,
.end = IRQ_SDC,
.flags = IORESOURCE_IRQ,
},
};
static struct resource puv3_unigfx_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_UNIGFX_BASE),
.end = io_v2p(PKUNITY_UNIGFX_BASE) + 0xfff,
.flags = IORESOURCE_MEM,
},
};
static struct resource puv3_rtc_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_RTC_BASE),
.end = io_v2p(PKUNITY_RTC_BASE) + 0xff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_RTCAlarm,
.end = IRQ_RTCAlarm,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = IRQ_RTC,
.end = IRQ_RTC,
.flags = IORESOURCE_IRQ
}
};
static struct resource puv3_pwm_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_OST_BASE) + 0x80,
.end = io_v2p(PKUNITY_OST_BASE) + 0xff,
.flags = IORESOURCE_MEM,
},
};
static struct resource puv3_uart0_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_UART0_BASE),
.end = io_v2p(PKUNITY_UART0_BASE) + 0xff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_UART0,
.end = IRQ_UART0,
.flags = IORESOURCE_IRQ
}
};
static struct resource puv3_uart1_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_UART1_BASE),
.end = io_v2p(PKUNITY_UART1_BASE) + 0xff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_UART1,
.end = IRQ_UART1,
.flags = IORESOURCE_IRQ
}
};
static struct resource puv3_umal_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_UMAL_BASE),
.end = io_v2p(PKUNITY_UMAL_BASE) + 0x1fff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_UMAL,
.end = IRQ_UMAL,
.flags = IORESOURCE_IRQ
}
};
#ifdef CONFIG_PUV3_PM
#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x
#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]
/*
* List of global PXA peripheral registers to preserve.
* More ones like CP and general purpose register values are preserved
* with the stack pointer in sleep.S.
*/
enum {
SLEEP_SAVE_PM_PLLDDRCFG,
SLEEP_SAVE_COUNT
};
static void puv3_cpu_pm_save(unsigned long *sleep_save)
{
/* SAVE(PM_PLLDDRCFG); */
}
static void puv3_cpu_pm_restore(unsigned long *sleep_save)
{
/* RESTORE(PM_PLLDDRCFG); */
}
static int puv3_cpu_pm_prepare(void)
{
/* set resume return address */
writel(virt_to_phys(puv3_cpu_resume), PM_DIVCFG);
return 0;
}
static void puv3_cpu_pm_enter(suspend_state_t state)
{
/* Clear reset status */
writel(RESETC_RSSR_HWR | RESETC_RSSR_WDR
| RESETC_RSSR_SMR | RESETC_RSSR_SWR, RESETC_RSSR);
switch (state) {
/* case PM_SUSPEND_ON:
puv3_cpu_idle();
break; */
case PM_SUSPEND_MEM:
puv3_cpu_pm_prepare();
puv3_cpu_suspend(PM_PMCR_SFB);
break;
}
}
static int puv3_cpu_pm_valid(suspend_state_t state)
{
return state == PM_SUSPEND_MEM;
}
static void puv3_cpu_pm_finish(void)
{
/* ensure not to come back here if it wasn't intended */
/* PSPR = 0; */
}
static struct puv3_cpu_pm_fns puv3_cpu_pm_fnss = {
.save_count = SLEEP_SAVE_COUNT,
.valid = puv3_cpu_pm_valid,
.save = puv3_cpu_pm_save,
.restore = puv3_cpu_pm_restore,
.enter = puv3_cpu_pm_enter,
.prepare = puv3_cpu_pm_prepare,
.finish = puv3_cpu_pm_finish,
};
static void __init puv3_init_pm(void)
{
puv3_cpu_pm_fns = &puv3_cpu_pm_fnss;
}
#else
static inline void puv3_init_pm(void) {}
#endif
void puv3_ps2_init(void)
{
struct clk *bclk32;
bclk32 = clk_get(NULL, "BUS32_CLK");
writel(clk_get_rate(bclk32) / 200000, PS2_CNT); /* should > 5us */
}
void __init puv3_core_init(void)
{
puv3_init_pm();
puv3_ps2_init();
platform_device_register_simple("PKUnity-v3-RTC", -1,
puv3_rtc_resources, ARRAY_SIZE(puv3_rtc_resources));
platform_device_register_simple("PKUnity-v3-UMAL", -1,
puv3_umal_resources, ARRAY_SIZE(puv3_umal_resources));
platform_device_register_simple("PKUnity-v3-MMC", -1,
puv3_mmc_resources, ARRAY_SIZE(puv3_mmc_resources));
platform_device_register_simple("PKUnity-v3-UNIGFX", -1,
puv3_unigfx_resources, ARRAY_SIZE(puv3_unigfx_resources));
platform_device_register_simple("PKUnity-v3-PWM", -1,
puv3_pwm_resources, ARRAY_SIZE(puv3_pwm_resources));
platform_device_register_simple("PKUnity-v3-UART", 0,
puv3_uart0_resources, ARRAY_SIZE(puv3_uart0_resources));
platform_device_register_simple("PKUnity-v3-UART", 1,
puv3_uart1_resources, ARRAY_SIZE(puv3_uart1_resources));
platform_device_register_simple("PKUnity-v3-AC97", -1, NULL, 0);
platform_device_register_resndata(&platform_bus, "musb_hdrc", -1,
puv3_usb_resources, ARRAY_SIZE(puv3_usb_resources),
&puv3_usb_plat, sizeof(puv3_usb_plat));
}

View File

@@ -0,0 +1,144 @@
/*
* linux/arch/unicore32/kernel/puv3-nb0916.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
#include <linux/io.h>
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio.h>
#include <linux/gpio_keys.h>
#include <linux/input.h>
#include <mach/hardware.h>
static struct physmap_flash_data physmap_flash_data = {
.width = 1,
};
static struct resource physmap_flash_resource = {
.start = 0xFFF80000,
.end = 0xFFFFFFFF,
.flags = IORESOURCE_MEM,
};
static struct resource puv3_i2c_resources[] = {
[0] = {
.start = io_v2p(PKUNITY_I2C_BASE),
.end = io_v2p(PKUNITY_I2C_BASE) + 0xff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_I2C,
.end = IRQ_I2C,
.flags = IORESOURCE_IRQ,
}
};
static struct platform_pwm_backlight_data nb0916_backlight_data = {
.pwm_id = 0,
.max_brightness = 100,
.dft_brightness = 100,
.pwm_period_ns = 70 * 1024,
};
static struct gpio_keys_button nb0916_gpio_keys[] = {
{
.type = EV_KEY,
.code = KEY_POWER,
.gpio = GPI_SOFF_REQ,
.desc = "Power Button",
.wakeup = 1,
.active_low = 1,
},
{
.type = EV_KEY,
.code = BTN_TOUCH,
.gpio = GPI_BTN_TOUCH,
.desc = "Touchpad Button",
.wakeup = 1,
.active_low = 1,
},
};
static struct gpio_keys_platform_data nb0916_gpio_button_data = {
.buttons = nb0916_gpio_keys,
.nbuttons = ARRAY_SIZE(nb0916_gpio_keys),
};
static irqreturn_t nb0916_lcdcaseoff_handler(int irq, void *dev_id)
{
if (gpio_get_value(GPI_LCD_CASE_OFF))
gpio_set_value(GPO_LCD_EN, 1);
else
gpio_set_value(GPO_LCD_EN, 0);
return IRQ_HANDLED;
}
static irqreturn_t nb0916_overheat_handler(int irq, void *dev_id)
{
machine_halt();
/* SYSTEM HALT, NO RETURN */
return IRQ_HANDLED;
}
static struct i2c_board_info __initdata puv3_i2c_devices[] = {
{ I2C_BOARD_INFO("lm75", I2C_TAR_THERMAL), },
{ I2C_BOARD_INFO("bq27200", I2C_TAR_PWIC), },
{ I2C_BOARD_INFO("24c02", I2C_TAR_EEPROM), },
};
int __init mach_nb0916_init(void)
{
i2c_register_board_info(0, puv3_i2c_devices,
ARRAY_SIZE(puv3_i2c_devices));
platform_device_register_simple("PKUnity-v3-I2C", -1,
puv3_i2c_resources, ARRAY_SIZE(puv3_i2c_resources));
platform_device_register_data(&platform_bus, "pwm-backlight", -1,
&nb0916_backlight_data, sizeof(nb0916_backlight_data));
platform_device_register_data(&platform_bus, "gpio-keys", -1,
&nb0916_gpio_button_data, sizeof(nb0916_gpio_button_data));
platform_device_register_resndata(&platform_bus, "physmap-flash", -1,
&physmap_flash_resource, 1,
&physmap_flash_data, sizeof(physmap_flash_data));
if (request_irq(gpio_to_irq(GPI_LCD_CASE_OFF),
&nb0916_lcdcaseoff_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"NB0916 lcd case off", NULL) < 0) {
printk(KERN_DEBUG "LCD-Case-OFF IRQ %d not available\n",
gpio_to_irq(GPI_LCD_CASE_OFF));
}
if (request_irq(gpio_to_irq(GPI_OTP_INT), &nb0916_overheat_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
"NB0916 overheating protection", NULL) < 0) {
printk(KERN_DEBUG "Overheating Protection IRQ %d not available\n",
gpio_to_irq(GPI_OTP_INT));
}
return 0;
}
subsys_initcall_sync(mach_nb0916_init);

View File

@@ -0,0 +1,350 @@
/*
* linux/arch/unicore32/kernel/setup.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/utsname.h>
#include <linux/initrd.h>
#include <linux/console.h>
#include <linux/bootmem.h>
#include <linux/seq_file.h>
#include <linux/screen_info.h>
#include <linux/init.h>
#include <linux/root_dev.h>
#include <linux/cpu.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/memblock.h>
#include <linux/elf.h>
#include <linux/io.h>
#include <asm/cputype.h>
#include <asm/sections.h>
#include <asm/setup.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm/traps.h>
#include <asm/memblock.h>
#include "setup.h"
#ifndef MEM_SIZE
#define MEM_SIZE (16*1024*1024)
#endif
struct stack {
u32 irq[3];
u32 abt[3];
u32 und[3];
} ____cacheline_aligned;
static struct stack stacks[NR_CPUS];
char elf_platform[ELF_PLATFORM_SIZE];
EXPORT_SYMBOL(elf_platform);
static char __initdata cmd_line[COMMAND_LINE_SIZE];
static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
/*
* Standard memory resources
*/
static struct resource mem_res[] = {
{
.name = "Kernel code",
.start = 0,
.end = 0,
.flags = IORESOURCE_MEM
},
{
.name = "Kernel data",
.start = 0,
.end = 0,
.flags = IORESOURCE_MEM
}
};
#define kernel_code mem_res[0]
#define kernel_data mem_res[1]
/*
* These functions re-use the assembly code in head.S, which
* already provide the required functionality.
*/
static void __init setup_processor(void)
{
printk(KERN_DEFAULT "CPU: UniCore-II [%08x] revision %d, cr=%08lx\n",
uc32_cpuid, (int)(uc32_cpuid >> 16) & 15, cr_alignment);
sprintf(init_utsname()->machine, "puv3");
sprintf(elf_platform, "ucv2");
}
/*
* cpu_init - initialise one CPU.
*
* cpu_init sets up the per-CPU stacks.
*/
void cpu_init(void)
{
unsigned int cpu = smp_processor_id();
struct stack *stk = &stacks[cpu];
/*
* setup stacks for re-entrant exception handlers
*/
__asm__ (
"mov.a asr, %1\n\t"
"add sp, %0, %2\n\t"
"mov.a asr, %3\n\t"
"add sp, %0, %4\n\t"
"mov.a asr, %5\n\t"
"add sp, %0, %6\n\t"
"mov.a asr, %7"
:
: "r" (stk),
"r" (PSR_R_BIT | PSR_I_BIT | INTR_MODE),
"I" (offsetof(struct stack, irq[0])),
"r" (PSR_R_BIT | PSR_I_BIT | ABRT_MODE),
"I" (offsetof(struct stack, abt[0])),
"r" (PSR_R_BIT | PSR_I_BIT | EXTN_MODE),
"I" (offsetof(struct stack, und[0])),
"r" (PSR_R_BIT | PSR_I_BIT | PRIV_MODE)
: "r30", "cc");
}
static int __init uc32_add_memory(unsigned long start, unsigned long size)
{
struct membank *bank = &meminfo.bank[meminfo.nr_banks];
if (meminfo.nr_banks >= NR_BANKS) {
printk(KERN_CRIT "NR_BANKS too low, "
"ignoring memory at %#lx\n", start);
return -EINVAL;
}
/*
* Ensure that start/size are aligned to a page boundary.
* Size is appropriately rounded down, start is rounded up.
*/
size -= start & ~PAGE_MASK;
bank->start = PAGE_ALIGN(start);
bank->size = size & PAGE_MASK;
/*
* Check whether this memory region has non-zero size or
* invalid node number.
*/
if (bank->size == 0)
return -EINVAL;
meminfo.nr_banks++;
return 0;
}
/*
* Pick out the memory size. We look for mem=size@start,
* where start and size are "size[KkMm]"
*/
static int __init early_mem(char *p)
{
static int usermem __initdata = 1;
unsigned long size, start;
char *endp;
/*
* If the user specifies memory size, we
* blow away any automatically generated
* size.
*/
if (usermem) {
usermem = 0;
meminfo.nr_banks = 0;
}
start = PHYS_OFFSET;
size = memparse(p, &endp);
if (*endp == '@')
start = memparse(endp + 1, NULL);
uc32_add_memory(start, size);
return 0;
}
early_param("mem", early_mem);
static void __init
request_standard_resources(struct meminfo *mi)
{
struct resource *res;
int i;
kernel_code.start = virt_to_phys(_stext);
kernel_code.end = virt_to_phys(_etext - 1);
kernel_data.start = virt_to_phys(_sdata);
kernel_data.end = virt_to_phys(_end - 1);
for (i = 0; i < mi->nr_banks; i++) {
if (mi->bank[i].size == 0)
continue;
res = alloc_bootmem_low(sizeof(*res));
res->name = "System RAM";
res->start = mi->bank[i].start;
res->end = mi->bank[i].start + mi->bank[i].size - 1;
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
request_resource(&iomem_resource, res);
if (kernel_code.start >= res->start &&
kernel_code.end <= res->end)
request_resource(res, &kernel_code);
if (kernel_data.start >= res->start &&
kernel_data.end <= res->end)
request_resource(res, &kernel_data);
}
}
static void (*init_machine)(void) __initdata;
static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (init_machine)
init_machine();
return 0;
}
arch_initcall(customize_machine);
void __init setup_arch(char **cmdline_p)
{
char *from = default_command_line;
setup_processor();
init_mm.start_code = (unsigned long) _stext;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
init_mm.brk = (unsigned long) _end;
/* parse_early_param needs a boot_command_line */
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
/* populate cmd_line too for later use, preserving boot_command_line */
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
parse_early_param();
uc32_memblock_init(&meminfo);
paging_init();
request_standard_resources(&meminfo);
cpu_init();
/*
* Set up various architecture-specific pointers
*/
init_machine = puv3_core_init;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
early_trap_init();
}
static struct cpu cpuinfo_unicore;
static int __init topology_init(void)
{
int i;
for_each_possible_cpu(i)
register_cpu(&cpuinfo_unicore, i);
return 0;
}
subsys_initcall(topology_init);
#ifdef CONFIG_HAVE_PROC_CPU
static int __init proc_cpu_init(void)
{
struct proc_dir_entry *res;
res = proc_mkdir("cpu", NULL);
if (!res)
return -ENOMEM;
return 0;
}
fs_initcall(proc_cpu_init);
#endif
static int c_show(struct seq_file *m, void *v)
{
seq_printf(m, "Processor\t: UniCore-II rev %d (%s)\n",
(int)(uc32_cpuid >> 16) & 15, elf_platform);
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
loops_per_jiffy / (500000/HZ),
(loops_per_jiffy / (5000/HZ)) % 100);
/* dump out the processor features */
seq_puts(m, "Features\t: CMOV UC-F64");
seq_printf(m, "\nCPU implementer\t: 0x%02x\n", uc32_cpuid >> 24);
seq_printf(m, "CPU architecture: 2\n");
seq_printf(m, "CPU revision\t: %d\n", (uc32_cpuid >> 16) & 15);
seq_printf(m, "Cache type\t: write-back\n"
"Cache clean\t: cp0 c5 ops\n"
"Cache lockdown\t: not support\n"
"Cache format\t: Harvard\n");
seq_puts(m, "\n");
seq_printf(m, "Hardware\t: PKUnity v3\n");
return 0;
}
static void *c_start(struct seq_file *m, loff_t *pos)
{
return *pos < 1 ? (void *)1 : NULL;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return NULL;
}
static void c_stop(struct seq_file *m, void *v)
{
}
const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = c_show
};

View File

@@ -0,0 +1,39 @@
/*
* linux/arch/unicore32/kernel/setup.h
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __UNICORE_KERNEL_SETUP_H__
#define __UNICORE_KERNEL_SETUP_H__
#include <asm/hwdef-copro.h>
extern void paging_init(void);
extern void puv3_core_init(void);
extern void cpu_init(void);
extern void puv3_ps2_init(void);
extern void pci_puv3_preinit(void);
extern void __init puv3_init_gpio(void);
extern void setup_mm_for_reboot(char mode);
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern void kernel_thread_helper(void);
extern void __init early_signal_init(void);
extern asmlinkage void __backtrace(void);
extern asmlinkage void c_backtrace(unsigned long fp, int pmode);
extern void __show_regs(struct pt_regs *);
#endif

View File

@@ -0,0 +1,441 @@
/*
* linux/arch/unicore32/kernel/signal.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/personality.h>
#include <linux/uaccess.h>
#include <linux/tracehook.h>
#include <linux/elf.h>
#include <linux/unistd.h>
#include <asm/cacheflush.h>
#include <asm/ucontext.h>
/*
* For UniCore syscalls, we encode the syscall number into the instruction.
*/
#define SWI_SYS_SIGRETURN (0xff000000) /* error number for new abi */
#define SWI_SYS_RT_SIGRETURN (0xff000000 | (__NR_rt_sigreturn))
#define SWI_SYS_RESTART (0xff000000 | (__NR_restart_syscall))
#define KERN_SIGRETURN_CODE (KUSER_VECPAGE_BASE + 0x00000500)
#define KERN_RESTART_CODE (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes))
const unsigned long sigreturn_codes[3] = {
SWI_SYS_SIGRETURN, SWI_SYS_RT_SIGRETURN,
};
const unsigned long syscall_restart_code[2] = {
SWI_SYS_RESTART, /* swi __NR_restart_syscall */
0x69efc004, /* ldr pc, [sp], #4 */
};
/*
* Do a signal return; undo the signal stack. These are aligned to 64-bit.
*/
struct sigframe {
struct ucontext uc;
unsigned long retcode[2];
};
struct rt_sigframe {
struct siginfo info;
struct sigframe sig;
};
static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
{
sigset_t set;
int err;
err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
if (err == 0)
set_current_blocked(&set);
err |= __get_user(regs->UCreg_00, &sf->uc.uc_mcontext.regs.UCreg_00);
err |= __get_user(regs->UCreg_01, &sf->uc.uc_mcontext.regs.UCreg_01);
err |= __get_user(regs->UCreg_02, &sf->uc.uc_mcontext.regs.UCreg_02);
err |= __get_user(regs->UCreg_03, &sf->uc.uc_mcontext.regs.UCreg_03);
err |= __get_user(regs->UCreg_04, &sf->uc.uc_mcontext.regs.UCreg_04);
err |= __get_user(regs->UCreg_05, &sf->uc.uc_mcontext.regs.UCreg_05);
err |= __get_user(regs->UCreg_06, &sf->uc.uc_mcontext.regs.UCreg_06);
err |= __get_user(regs->UCreg_07, &sf->uc.uc_mcontext.regs.UCreg_07);
err |= __get_user(regs->UCreg_08, &sf->uc.uc_mcontext.regs.UCreg_08);
err |= __get_user(regs->UCreg_09, &sf->uc.uc_mcontext.regs.UCreg_09);
err |= __get_user(regs->UCreg_10, &sf->uc.uc_mcontext.regs.UCreg_10);
err |= __get_user(regs->UCreg_11, &sf->uc.uc_mcontext.regs.UCreg_11);
err |= __get_user(regs->UCreg_12, &sf->uc.uc_mcontext.regs.UCreg_12);
err |= __get_user(regs->UCreg_13, &sf->uc.uc_mcontext.regs.UCreg_13);
err |= __get_user(regs->UCreg_14, &sf->uc.uc_mcontext.regs.UCreg_14);
err |= __get_user(regs->UCreg_15, &sf->uc.uc_mcontext.regs.UCreg_15);
err |= __get_user(regs->UCreg_16, &sf->uc.uc_mcontext.regs.UCreg_16);
err |= __get_user(regs->UCreg_17, &sf->uc.uc_mcontext.regs.UCreg_17);
err |= __get_user(regs->UCreg_18, &sf->uc.uc_mcontext.regs.UCreg_18);
err |= __get_user(regs->UCreg_19, &sf->uc.uc_mcontext.regs.UCreg_19);
err |= __get_user(regs->UCreg_20, &sf->uc.uc_mcontext.regs.UCreg_20);
err |= __get_user(regs->UCreg_21, &sf->uc.uc_mcontext.regs.UCreg_21);
err |= __get_user(regs->UCreg_22, &sf->uc.uc_mcontext.regs.UCreg_22);
err |= __get_user(regs->UCreg_23, &sf->uc.uc_mcontext.regs.UCreg_23);
err |= __get_user(regs->UCreg_24, &sf->uc.uc_mcontext.regs.UCreg_24);
err |= __get_user(regs->UCreg_25, &sf->uc.uc_mcontext.regs.UCreg_25);
err |= __get_user(regs->UCreg_26, &sf->uc.uc_mcontext.regs.UCreg_26);
err |= __get_user(regs->UCreg_fp, &sf->uc.uc_mcontext.regs.UCreg_fp);
err |= __get_user(regs->UCreg_ip, &sf->uc.uc_mcontext.regs.UCreg_ip);
err |= __get_user(regs->UCreg_sp, &sf->uc.uc_mcontext.regs.UCreg_sp);
err |= __get_user(regs->UCreg_lr, &sf->uc.uc_mcontext.regs.UCreg_lr);
err |= __get_user(regs->UCreg_pc, &sf->uc.uc_mcontext.regs.UCreg_pc);
err |= __get_user(regs->UCreg_asr, &sf->uc.uc_mcontext.regs.UCreg_asr);
err |= !valid_user_regs(regs);
return err;
}
asmlinkage int __sys_rt_sigreturn(struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
/* Always make any pending restarted system calls return -EINTR */
current_thread_info()->restart_block.fn = do_no_restart_syscall;
/*
* Since we stacked the signal on a 64-bit boundary,
* then 'sp' should be word aligned here. If it's
* not, then the user is trying to mess with us.
*/
if (regs->UCreg_sp & 7)
goto badframe;
frame = (struct rt_sigframe __user *)regs->UCreg_sp;
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (restore_sigframe(regs, &frame->sig))
goto badframe;
if (restore_altstack(&frame->sig.uc.uc_stack))
goto badframe;
return regs->UCreg_00;
badframe:
force_sig(SIGSEGV, current);
return 0;
}
static int setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs,
sigset_t *set)
{
int err = 0;
err |= __put_user(regs->UCreg_00, &sf->uc.uc_mcontext.regs.UCreg_00);
err |= __put_user(regs->UCreg_01, &sf->uc.uc_mcontext.regs.UCreg_01);
err |= __put_user(regs->UCreg_02, &sf->uc.uc_mcontext.regs.UCreg_02);
err |= __put_user(regs->UCreg_03, &sf->uc.uc_mcontext.regs.UCreg_03);
err |= __put_user(regs->UCreg_04, &sf->uc.uc_mcontext.regs.UCreg_04);
err |= __put_user(regs->UCreg_05, &sf->uc.uc_mcontext.regs.UCreg_05);
err |= __put_user(regs->UCreg_06, &sf->uc.uc_mcontext.regs.UCreg_06);
err |= __put_user(regs->UCreg_07, &sf->uc.uc_mcontext.regs.UCreg_07);
err |= __put_user(regs->UCreg_08, &sf->uc.uc_mcontext.regs.UCreg_08);
err |= __put_user(regs->UCreg_09, &sf->uc.uc_mcontext.regs.UCreg_09);
err |= __put_user(regs->UCreg_10, &sf->uc.uc_mcontext.regs.UCreg_10);
err |= __put_user(regs->UCreg_11, &sf->uc.uc_mcontext.regs.UCreg_11);
err |= __put_user(regs->UCreg_12, &sf->uc.uc_mcontext.regs.UCreg_12);
err |= __put_user(regs->UCreg_13, &sf->uc.uc_mcontext.regs.UCreg_13);
err |= __put_user(regs->UCreg_14, &sf->uc.uc_mcontext.regs.UCreg_14);
err |= __put_user(regs->UCreg_15, &sf->uc.uc_mcontext.regs.UCreg_15);
err |= __put_user(regs->UCreg_16, &sf->uc.uc_mcontext.regs.UCreg_16);
err |= __put_user(regs->UCreg_17, &sf->uc.uc_mcontext.regs.UCreg_17);
err |= __put_user(regs->UCreg_18, &sf->uc.uc_mcontext.regs.UCreg_18);
err |= __put_user(regs->UCreg_19, &sf->uc.uc_mcontext.regs.UCreg_19);
err |= __put_user(regs->UCreg_20, &sf->uc.uc_mcontext.regs.UCreg_20);
err |= __put_user(regs->UCreg_21, &sf->uc.uc_mcontext.regs.UCreg_21);
err |= __put_user(regs->UCreg_22, &sf->uc.uc_mcontext.regs.UCreg_22);
err |= __put_user(regs->UCreg_23, &sf->uc.uc_mcontext.regs.UCreg_23);
err |= __put_user(regs->UCreg_24, &sf->uc.uc_mcontext.regs.UCreg_24);
err |= __put_user(regs->UCreg_25, &sf->uc.uc_mcontext.regs.UCreg_25);
err |= __put_user(regs->UCreg_26, &sf->uc.uc_mcontext.regs.UCreg_26);
err |= __put_user(regs->UCreg_fp, &sf->uc.uc_mcontext.regs.UCreg_fp);
err |= __put_user(regs->UCreg_ip, &sf->uc.uc_mcontext.regs.UCreg_ip);
err |= __put_user(regs->UCreg_sp, &sf->uc.uc_mcontext.regs.UCreg_sp);
err |= __put_user(regs->UCreg_lr, &sf->uc.uc_mcontext.regs.UCreg_lr);
err |= __put_user(regs->UCreg_pc, &sf->uc.uc_mcontext.regs.UCreg_pc);
err |= __put_user(regs->UCreg_asr, &sf->uc.uc_mcontext.regs.UCreg_asr);
err |= __put_user(current->thread.trap_no,
&sf->uc.uc_mcontext.trap_no);
err |= __put_user(current->thread.error_code,
&sf->uc.uc_mcontext.error_code);
err |= __put_user(current->thread.address,
&sf->uc.uc_mcontext.fault_address);
err |= __put_user(set->sig[0], &sf->uc.uc_mcontext.oldmask);
err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
return err;
}
static inline void __user *get_sigframe(struct k_sigaction *ka,
struct pt_regs *regs, int framesize)
{
unsigned long sp = regs->UCreg_sp;
void __user *frame;
/*
* This is the X/Open sanctioned signal stack switching.
*/
if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
sp = current->sas_ss_sp + current->sas_ss_size;
/*
* ATPCS B01 mandates 8-byte alignment
*/
frame = (void __user *)((sp - framesize) & ~7);
/*
* Check that we can actually write to the signal frame.
*/
if (!access_ok(VERIFY_WRITE, frame, framesize))
frame = NULL;
return frame;
}
static int setup_return(struct pt_regs *regs, struct k_sigaction *ka,
unsigned long __user *rc, void __user *frame, int usig)
{
unsigned long handler = (unsigned long)ka->sa.sa_handler;
unsigned long retcode;
unsigned long asr = regs->UCreg_asr & ~PSR_f;
unsigned int idx = 0;
if (ka->sa.sa_flags & SA_SIGINFO)
idx += 1;
if (__put_user(sigreturn_codes[idx], rc) ||
__put_user(sigreturn_codes[idx+1], rc+1))
return 1;
retcode = KERN_SIGRETURN_CODE + (idx << 2);
regs->UCreg_00 = usig;
regs->UCreg_sp = (unsigned long)frame;
regs->UCreg_lr = retcode;
regs->UCreg_pc = handler;
regs->UCreg_asr = asr;
return 0;
}
static int setup_frame(int usig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs *regs)
{
struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
int err = 0;
if (!frame)
return 1;
/*
* Set uc.uc_flags to a value which sc.trap_no would never have.
*/
err |= __put_user(0x5ac3c35a, &frame->uc.uc_flags);
err |= setup_sigframe(frame, regs, set);
if (err == 0)
err |= setup_return(regs, ka, frame->retcode, frame, usig);
return err;
}
static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame =
get_sigframe(ka, regs, sizeof(*frame));
int err = 0;
if (!frame)
return 1;
err |= copy_siginfo_to_user(&frame->info, info);
err |= __put_user(0, &frame->sig.uc.uc_flags);
err |= __put_user(NULL, &frame->sig.uc.uc_link);
err |= __save_altstack(&frame->sig.uc.uc_stack, regs->UCreg_sp);
err |= setup_sigframe(&frame->sig, regs, set);
if (err == 0)
err |= setup_return(regs, ka, frame->sig.retcode, frame, usig);
if (err == 0) {
/*
* For realtime signals we must also set the second and third
* arguments for the signal handler.
*/
regs->UCreg_01 = (unsigned long)&frame->info;
regs->UCreg_02 = (unsigned long)&frame->sig.uc;
}
return err;
}
static inline void setup_syscall_restart(struct pt_regs *regs)
{
regs->UCreg_00 = regs->UCreg_ORIG_00;
regs->UCreg_pc -= 4;
}
/*
* OK, we're invoking a handler
*/
static void handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, struct pt_regs *regs, int syscall)
{
struct thread_info *thread = current_thread_info();
struct task_struct *tsk = current;
sigset_t *oldset = sigmask_to_save();
int usig = sig;
int ret;
/*
* If we were from a system call, check for system call restarting...
*/
if (syscall) {
switch (regs->UCreg_00) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->UCreg_00 = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->UCreg_00 = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
setup_syscall_restart(regs);
}
}
/*
* translate the signal
*/
if (usig < 32 && thread->exec_domain
&& thread->exec_domain->signal_invmap)
usig = thread->exec_domain->signal_invmap[usig];
/*
* Set up the stack frame
*/
if (ka->sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(usig, ka, info, oldset, regs);
else
ret = setup_frame(usig, ka, oldset, regs);
/*
* Check that the resulting registers are actually sane.
*/
ret |= !valid_user_regs(regs);
if (ret != 0) {
force_sigsegv(sig, tsk);
return;
}
signal_delivered(sig, info, ka, regs, 0);
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* Note that we go through the signals twice: once to check the signals that
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
static void do_signal(struct pt_regs *regs, int syscall)
{
struct k_sigaction ka;
siginfo_t info;
int signr;
/*
* We want the common case to go fast, which
* is why we may in certain cases get here from
* kernel mode. Just return without doing anything
* if so.
*/
if (!user_mode(regs))
return;
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
handle_signal(signr, &ka, &info, regs, syscall);
return;
}
/*
* No signal to deliver to the process - restart the syscall.
*/
if (syscall) {
if (regs->UCreg_00 == -ERESTART_RESTARTBLOCK) {
u32 __user *usp;
regs->UCreg_sp -= 4;
usp = (u32 __user *)regs->UCreg_sp;
if (put_user(regs->UCreg_pc, usp) == 0) {
regs->UCreg_pc = KERN_RESTART_CODE;
} else {
regs->UCreg_sp += 4;
force_sigsegv(0, current);
}
}
if (regs->UCreg_00 == -ERESTARTNOHAND ||
regs->UCreg_00 == -ERESTARTSYS ||
regs->UCreg_00 == -ERESTARTNOINTR) {
setup_syscall_restart(regs);
}
}
/* If there's no signal to deliver, we just put the saved
* sigmask back.
*/
restore_saved_sigmask();
}
asmlinkage void do_notify_resume(struct pt_regs *regs,
unsigned int thread_flags, int syscall)
{
if (thread_flags & _TIF_SIGPENDING)
do_signal(regs, syscall);
if (thread_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs);
}
}
/*
* Copy signal return handlers into the vector page, and
* set sigreturn to be a pointer to these.
*/
void __init early_signal_init(void)
{
memcpy((void *)kuser_vecpage_to_vectors(KERN_SIGRETURN_CODE),
sigreturn_codes, sizeof(sigreturn_codes));
memcpy((void *)kuser_vecpage_to_vectors(KERN_RESTART_CODE),
syscall_restart_code, sizeof(syscall_restart_code));
/* Need not to flush icache, since early_trap_init will do it last. */
}

View File

@@ -0,0 +1,202 @@
/*
* linux/arch/unicore32/kernel/sleep.S
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <mach/hardware.h>
.text
pkunity_cpu_save_cp:
@ get coprocessor registers
movc r3, p0.c7, #0 @ PID
movc r4, p0.c2, #0 @ translation table base addr
movc r5, p0.c1, #0 @ control reg
@ store them plus current virtual stack ptr on stack
mov r6, sp
stm.w (r3 - r6), [sp-]
mov pc, lr
pkunity_cpu_save_sp:
@ preserve phys address of stack
mov r0, sp
stw.w lr, [sp+], #-4
b.l sleep_phys_sp
ldw r1, =sleep_save_sp
stw r0, [r1]
ldw.w pc, [sp]+, #4
/*
* puv3_cpu_suspend()
*
* Forces CPU into sleep state.
*
* r0 = value for PWRMODE M field for desired sleep state
*/
ENTRY(puv3_cpu_suspend)
stm.w (r16 - r27, lr), [sp-] @ save registers on stack
stm.w (r4 - r15), [sp-] @ save registers on stack
#ifdef CONFIG_UNICORE_FPU_F64
sfm.w (f0 - f7 ), [sp-]
sfm.w (f8 - f15), [sp-]
sfm.w (f16 - f23), [sp-]
sfm.w (f24 - f31), [sp-]
cff r4, s31
stm.w (r4), [sp-]
#endif
b.l pkunity_cpu_save_cp
b.l pkunity_cpu_save_sp
@ clean data cache
mov r1, #0
movc p0.c5, r1, #14
nop
nop
nop
nop
@ DDR2 BaseAddr
ldw r0, =(PKUNITY_DDR2CTRL_BASE)
@ PM BaseAddr
ldw r1, =(PKUNITY_PM_BASE)
@ set PLL_SYS_CFG reg, 275
movl r6, #0x00002401
stw r6, [r1+], #0x18
@ set PLL_DDR_CFG reg, 66MHz
movl r6, #0x00100c00
stw r6, [r1+], #0x1c
@ set wake up source
movl r8, #0x800001ff @ epip4d
stw r8, [r1+], #0xc
@ set PGSR
movl r5, #0x40000
stw r5, [r1+], #0x10
@ prepare DDR2 refresh settings
ldw r5, [r0+], #0x24
or r5, r5, #0x00000001
@ prepare PMCR for PLL changing
movl r6, #0xc
@ prepare for closing PLL
movl r7, #0x1
@ prepare sleep mode
mov r8, #0x1
@ movl r0, 0x11111111
@ put_word_ocd r0
b pkunity_cpu_do_suspend
.ltorg
.align 5
pkunity_cpu_do_suspend:
b 101f
@ put DDR2 into self-refresh
100: stw r5, [r0+], #0x24
@ change PLL
stw r6, [r1]
b 1f
.ltorg
.align 5
101: b 102f
@ wait for PLL changing complete
1: ldw r6, [r1+], #0x44
csub.a r6, #0x1
bne 1b
b 2f
.ltorg
.align 5
102: b 100b
@ close PLL
2: stw r7, [r1+], #0x4
@ enter sleep mode
stw r8, [r1]
3: b 3b
/*
* puv3_cpu_resume()
*
* entry point from bootloader into kernel during resume
*
* Note: Yes, part of the following code is located into the .data section.
* This is to allow sleep_save_sp to be accessed with a relative load
* while we can't rely on any MMU translation. We could have put
* sleep_save_sp in the .text section as well, but some setups might
* insist on it to be truly read-only.
*/
.data
.align 5
ENTRY(puv3_cpu_resume)
@ movl r0, 0x20202020
@ put_word_ocd r0
ldw r0, sleep_save_sp @ stack phys addr
ldw r2, =resume_after_mmu @ its absolute virtual address
ldm (r3 - r6), [r0]+ @ CP regs + virt stack ptr
mov sp, r6 @ CP regs + virt stack ptr
mov r1, #0
movc p0.c6, r1, #6 @ invalidate I & D TLBs
movc p0.c5, r1, #28 @ invalidate I & D caches, BTB
movc p0.c7, r3, #0 @ PID
movc p0.c2, r4, #0 @ translation table base addr
movc p0.c1, r5, #0 @ control reg, turn on mmu
nop
jump r2
nop
nop
nop
nop
nop
sleep_save_sp:
.word 0 @ preserve stack phys ptr here
.text
resume_after_mmu:
@ movl r0, 0x30303030
@ put_word_ocd r0
#ifdef CONFIG_UNICORE_FPU_F64
lfm.w (f0 - f7 ), [sp]+
lfm.w (f8 - f15), [sp]+
lfm.w (f16 - f23), [sp]+
lfm.w (f24 - f31), [sp]+
ldm.w (r4), [sp]+
ctf r4, s31
#endif
ldm.w (r4 - r15), [sp]+ @ restore registers from stack
ldm.w (r16 - r27, pc), [sp]+ @ return to caller

View File

@@ -0,0 +1,131 @@
/*
* linux/arch/unicore32/kernel/stacktrace.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/stacktrace.h>
#include <asm/stacktrace.h>
#if defined(CONFIG_FRAME_POINTER)
/*
* Unwind the current stack frame and store the new register values in the
* structure passed as argument. Unwinding is equivalent to a function return,
* hence the new PC value rather than LR should be used for backtrace.
*
* With framepointer enabled, a simple function prologue looks like this:
* mov ip, sp
* stmdb sp!, {fp, ip, lr, pc}
* sub fp, ip, #4
*
* A simple function epilogue looks like this:
* ldm sp, {fp, sp, pc}
*
* Note that with framepointer enabled, even the leaf functions have the same
* prologue and epilogue, therefore we can ignore the LR value in this case.
*/
int notrace unwind_frame(struct stackframe *frame)
{
unsigned long high, low;
unsigned long fp = frame->fp;
/* only go to a higher address on the stack */
low = frame->sp;
high = ALIGN(low, THREAD_SIZE);
/* check current frame pointer is within bounds */
if (fp < (low + 12) || fp + 4 >= high)
return -EINVAL;
/* restore the registers from the stack frame */
frame->fp = *(unsigned long *)(fp - 12);
frame->sp = *(unsigned long *)(fp - 8);
frame->pc = *(unsigned long *)(fp - 4);
return 0;
}
#endif
void notrace walk_stackframe(struct stackframe *frame,
int (*fn)(struct stackframe *, void *), void *data)
{
while (1) {
int ret;
if (fn(frame, data))
break;
ret = unwind_frame(frame);
if (ret < 0)
break;
}
}
EXPORT_SYMBOL(walk_stackframe);
#ifdef CONFIG_STACKTRACE
struct stack_trace_data {
struct stack_trace *trace;
unsigned int no_sched_functions;
unsigned int skip;
};
static int save_trace(struct stackframe *frame, void *d)
{
struct stack_trace_data *data = d;
struct stack_trace *trace = data->trace;
unsigned long addr = frame->pc;
if (data->no_sched_functions && in_sched_functions(addr))
return 0;
if (data->skip) {
data->skip--;
return 0;
}
trace->entries[trace->nr_entries++] = addr;
return trace->nr_entries >= trace->max_entries;
}
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
struct stack_trace_data data;
struct stackframe frame;
data.trace = trace;
data.skip = trace->skip;
if (tsk != current) {
data.no_sched_functions = 1;
frame.fp = thread_saved_fp(tsk);
frame.sp = thread_saved_sp(tsk);
frame.lr = 0; /* recovered from the stack */
frame.pc = thread_saved_pc(tsk);
} else {
register unsigned long current_sp asm("sp");
data.no_sched_functions = 0;
frame.fp = (unsigned long)__builtin_frame_address(0);
frame.sp = current_sp;
frame.lr = (unsigned long)__builtin_return_address(0);
frame.pc = (unsigned long)save_stack_trace_tsk;
}
walk_stackframe(&frame, save_trace, &data);
if (trace->nr_entries < trace->max_entries)
trace->entries[trace->nr_entries++] = ULONG_MAX;
}
void save_stack_trace(struct stack_trace *trace)
{
save_stack_trace_tsk(current, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace);
#endif

View File

@@ -0,0 +1,40 @@
/*
* linux/arch/unicore32/kernel/sys.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/sem.h>
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/stat.h>
#include <linux/syscalls.h>
#include <linux/mman.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/ipc.h>
#include <linux/uaccess.h>
#include <asm/syscalls.h>
#include <asm/cacheflush.h>
/* Provide the actual syscall number to call mapping. */
#undef __SYSCALL
#define __SYSCALL(nr, call) [nr] = (call),
#define sys_mmap2 sys_mmap_pgoff
/* Note that we don't include <linux/unistd.h> but <asm/unistd.h> */
void *sys_call_table[__NR_syscalls] = {
[0 ... __NR_syscalls-1] = sys_ni_syscall,
#include <asm/unistd.h>
};

View File

@@ -0,0 +1,143 @@
/*
* linux/arch/unicore32/kernel/time.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
* Copyright (C) 2001-2010 Guan Xuetao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/timex.h>
#include <linux/clockchips.h>
#include <mach/hardware.h>
#define MIN_OSCR_DELTA 2
static irqreturn_t puv3_ost0_interrupt(int irq, void *dev_id)
{
struct clock_event_device *c = dev_id;
/* Disarm the compare/match, signal the event. */
writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER);
writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR);
c->event_handler(c);
return IRQ_HANDLED;
}
static int
puv3_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c)
{
unsigned long next, oscr;
writel(readl(OST_OIER) | OST_OIER_E0, OST_OIER);
next = readl(OST_OSCR) + delta;
writel(next, OST_OSMR0);
oscr = readl(OST_OSCR);
return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0;
}
static void
puv3_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c)
{
switch (mode) {
case CLOCK_EVT_MODE_ONESHOT:
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_SHUTDOWN:
writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER);
writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR);
break;
case CLOCK_EVT_MODE_RESUME:
case CLOCK_EVT_MODE_PERIODIC:
break;
}
}
static struct clock_event_device ckevt_puv3_osmr0 = {
.name = "osmr0",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
.set_next_event = puv3_osmr0_set_next_event,
.set_mode = puv3_osmr0_set_mode,
};
static cycle_t puv3_read_oscr(struct clocksource *cs)
{
return readl(OST_OSCR);
}
static struct clocksource cksrc_puv3_oscr = {
.name = "oscr",
.rating = 200,
.read = puv3_read_oscr,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct irqaction puv3_timer_irq = {
.name = "ost0",
.flags = IRQF_TIMER | IRQF_IRQPOLL,
.handler = puv3_ost0_interrupt,
.dev_id = &ckevt_puv3_osmr0,
};
void __init time_init(void)
{
writel(0, OST_OIER); /* disable any timer interrupts */
writel(0, OST_OSSR); /* clear status on all timers */
clockevents_calc_mult_shift(&ckevt_puv3_osmr0, CLOCK_TICK_RATE, 5);
ckevt_puv3_osmr0.max_delta_ns =
clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0);
ckevt_puv3_osmr0.min_delta_ns =
clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1;
ckevt_puv3_osmr0.cpumask = cpumask_of(0);
setup_irq(IRQ_TIMER0, &puv3_timer_irq);
clocksource_register_hz(&cksrc_puv3_oscr, CLOCK_TICK_RATE);
clockevents_register_device(&ckevt_puv3_osmr0);
}
#ifdef CONFIG_PM
unsigned long osmr[4], oier;
void puv3_timer_suspend(void)
{
osmr[0] = readl(OST_OSMR0);
osmr[1] = readl(OST_OSMR1);
osmr[2] = readl(OST_OSMR2);
osmr[3] = readl(OST_OSMR3);
oier = readl(OST_OIER);
}
void puv3_timer_resume(void)
{
writel(0, OST_OSSR);
writel(osmr[0], OST_OSMR0);
writel(osmr[1], OST_OSMR1);
writel(osmr[2], OST_OSMR2);
writel(osmr[3], OST_OSMR3);
writel(oier, OST_OIER);
/*
* OSMR0 is the system timer: make sure OSCR is sufficiently behind
*/
writel(readl(OST_OSMR0) - LATCH, OST_OSCR);
}
#else
void puv3_timer_suspend(void) { };
void puv3_timer_resume(void) { };
#endif

View File

@@ -0,0 +1,324 @@
/*
* linux/arch/unicore32/kernel/traps.c
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 'traps.c' handles hardware exceptions after we have saved some state.
* Mostly a debugging aid, but will probably kill the offending process.
*/
#include <linux/module.h>
#include <linux/signal.h>
#include <linux/spinlock.h>
#include <linux/personality.h>
#include <linux/kallsyms.h>
#include <linux/kdebug.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/hardirq.h>
#include <linux/init.h>
#include <linux/atomic.h>
#include <linux/unistd.h>
#include <asm/cacheflush.h>
#include <asm/traps.h>
#include "setup.h"
static void dump_mem(const char *, const char *, unsigned long, unsigned long);
void dump_backtrace_entry(unsigned long where,
unsigned long from, unsigned long frame)
{
#ifdef CONFIG_KALLSYMS
printk(KERN_DEFAULT "[<%08lx>] (%pS) from [<%08lx>] (%pS)\n",
where, (void *)where, from, (void *)from);
#else
printk(KERN_DEFAULT "Function entered at [<%08lx>] from [<%08lx>]\n",
where, from);
#endif
}
/*
* Stack pointers should always be within the kernels view of
* physical memory. If it is not there, then we can't dump
* out any information relating to the stack.
*/
static int verify_stack(unsigned long sp)
{
if (sp < PAGE_OFFSET ||
(sp > (unsigned long)high_memory && high_memory != NULL))
return -EFAULT;
return 0;
}
/*
* Dump out the contents of some memory nicely...
*/
static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
unsigned long top)
{
unsigned long first;
mm_segment_t fs;
int i;
/*
* We need to switch to kernel mode so that we can use __get_user
* to safely read from kernel space. Note that we now dump the
* code first, just in case the backtrace kills us.
*/
fs = get_fs();
set_fs(KERNEL_DS);
printk(KERN_DEFAULT "%s%s(0x%08lx to 0x%08lx)\n",
lvl, str, bottom, top);
for (first = bottom & ~31; first < top; first += 32) {
unsigned long p;
char str[sizeof(" 12345678") * 8 + 1];
memset(str, ' ', sizeof(str));
str[sizeof(str) - 1] = '\0';
for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {
if (p >= bottom && p < top) {
unsigned long val;
if (__get_user(val, (unsigned long *)p) == 0)
sprintf(str + i * 9, " %08lx", val);
else
sprintf(str + i * 9, " ????????");
}
}
printk(KERN_DEFAULT "%s%04lx:%s\n", lvl, first & 0xffff, str);
}
set_fs(fs);
}
static void dump_instr(const char *lvl, struct pt_regs *regs)
{
unsigned long addr = instruction_pointer(regs);
const int width = 8;
mm_segment_t fs;
char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
int i;
/*
* We need to switch to kernel mode so that we can use __get_user
* to safely read from kernel space. Note that we now dump the
* code first, just in case the backtrace kills us.
*/
fs = get_fs();
set_fs(KERNEL_DS);
for (i = -4; i < 1; i++) {
unsigned int val, bad;
bad = __get_user(val, &((u32 *)addr)[i]);
if (!bad)
p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
width, val);
else {
p += sprintf(p, "bad PC value");
break;
}
}
printk(KERN_DEFAULT "%sCode: %s\n", lvl, str);
set_fs(fs);
}
static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
unsigned int fp, mode;
int ok = 1;
printk(KERN_DEFAULT "Backtrace: ");
if (!tsk)
tsk = current;
if (regs) {
fp = regs->UCreg_fp;
mode = processor_mode(regs);
} else if (tsk != current) {
fp = thread_saved_fp(tsk);
mode = 0x10;
} else {
asm("mov %0, fp" : "=r" (fp) : : "cc");
mode = 0x10;
}
if (!fp) {
printk("no frame pointer");
ok = 0;
} else if (verify_stack(fp)) {
printk("invalid frame pointer 0x%08x", fp);
ok = 0;
} else if (fp < (unsigned long)end_of_stack(tsk))
printk("frame pointer underflow");
printk("\n");
if (ok)
c_backtrace(fp, mode);
}
void show_stack(struct task_struct *tsk, unsigned long *sp)
{
dump_backtrace(NULL, tsk);
barrier();
}
static int __die(const char *str, int err, struct thread_info *thread,
struct pt_regs *regs)
{
struct task_struct *tsk = thread->task;
static int die_counter;
int ret;
printk(KERN_EMERG "Internal error: %s: %x [#%d]\n",
str, err, ++die_counter);
/* trap and error numbers are mostly meaningless on UniCore */
ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, \
SIGSEGV);
if (ret == NOTIFY_STOP)
return ret;
print_modules();
__show_regs(regs);
printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n",
TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
if (!user_mode(regs) || in_interrupt()) {
dump_mem(KERN_EMERG, "Stack: ", regs->UCreg_sp,
THREAD_SIZE + (unsigned long)task_stack_page(tsk));
dump_backtrace(regs, tsk);
dump_instr(KERN_EMERG, regs);
}
return ret;
}
DEFINE_SPINLOCK(die_lock);
/*
* This function is protected against re-entrancy.
*/
void die(const char *str, struct pt_regs *regs, int err)
{
struct thread_info *thread = current_thread_info();
int ret;
oops_enter();
spin_lock_irq(&die_lock);
console_verbose();
bust_spinlocks(1);
ret = __die(str, err, thread, regs);
bust_spinlocks(0);
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
spin_unlock_irq(&die_lock);
oops_exit();
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
if (ret != NOTIFY_STOP)
do_exit(SIGSEGV);
}
void uc32_notify_die(const char *str, struct pt_regs *regs,
struct siginfo *info, unsigned long err, unsigned long trap)
{
if (user_mode(regs)) {
current->thread.error_code = err;
current->thread.trap_no = trap;
force_sig_info(info->si_signo, info, current);
} else
die(str, regs, err);
}
/*
* bad_mode handles the impossible case in the vectors. If you see one of
* these, then it's extremely serious, and could mean you have buggy hardware.
* It never returns, and never tries to sync. We hope that we can at least
* dump out some state information...
*/
asmlinkage void bad_mode(struct pt_regs *regs, unsigned int reason)
{
console_verbose();
printk(KERN_CRIT "Bad mode detected with reason 0x%x\n", reason);
die("Oops - bad mode", regs, 0);
local_irq_disable();
panic("bad mode");
}
void __pte_error(const char *file, int line, unsigned long val)
{
printk(KERN_DEFAULT "%s:%d: bad pte %08lx.\n", file, line, val);
}
void __pmd_error(const char *file, int line, unsigned long val)
{
printk(KERN_DEFAULT "%s:%d: bad pmd %08lx.\n", file, line, val);
}
void __pgd_error(const char *file, int line, unsigned long val)
{
printk(KERN_DEFAULT "%s:%d: bad pgd %08lx.\n", file, line, val);
}
asmlinkage void __div0(void)
{
printk(KERN_DEFAULT "Division by zero in kernel.\n");
dump_stack();
}
EXPORT_SYMBOL(__div0);
void abort(void)
{
BUG();
/* if that doesn't kill us, halt */
panic("Oops failed to kill thread");
}
EXPORT_SYMBOL(abort);
void __init trap_init(void)
{
return;
}
void __init early_trap_init(void)
{
unsigned long vectors = VECTORS_BASE;
/*
* Copy the vectors, stubs (in entry-unicore.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
*/
memcpy((void *)vectors,
__vectors_start,
__vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200,
__stubs_start,
__stubs_end - __stubs_start);
early_signal_init();
flush_icache_range(vectors, vectors + PAGE_SIZE);
}

View File

@@ -0,0 +1,62 @@
/*
* linux/arch/unicore32/kernel/vmlinux.lds.S
*
* Code specific to PKUnity SoC and UniCore ISA
*
* Copyright (C) 2001-2010 GUAN Xue-tao
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <asm-generic/vmlinux.lds.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
#include <asm/page.h>
#include <asm/cache.h>
OUTPUT_ARCH(unicore32)
ENTRY(stext)
jiffies = jiffies_64;
SECTIONS
{
. = PAGE_OFFSET + KERNEL_IMAGE_START;
_text = .;
__init_begin = .;
HEAD_TEXT_SECTION
INIT_TEXT_SECTION(PAGE_SIZE)
INIT_DATA_SECTION(16)
PERCPU_SECTION(L1_CACHE_BYTES)
__init_end = .;
_stext = .;
.text : { /* Real text segment */
TEXT_TEXT
SCHED_TEXT
LOCK_TEXT
*(.fixup)
*(.gnu.warning)
}
_etext = .;
_sdata = .;
RO_DATA_SECTION(PAGE_SIZE)
RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
_edata = .;
EXCEPTION_TABLE(L1_CACHE_BYTES)
NOTES
BSS_SECTION(0, 0, 0)
_end = .;
STABS_DEBUG
DWARF_DEBUG
DISCARDS /* Exit code and data */
}