Commit 64909882 authored by GuanXuetao's avatar GuanXuetao

unicore32 additional architecture files: pm related files

This patch adds pm related files, including hibernate and sleep supports.
Signed-off-by: default avatarGuan Xuetao <gxt@mprc.pku.edu.cn>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
parent f864d2f8
/*
* linux/arch/unicore32/include/asm/suspend.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_SUSPEND_H__
#define __UNICORE_SUSPEND_H__
#ifndef __ASSEMBLY__
static inline int arch_prepare_suspend(void) { return 0; }
#include <asm/ptrace.h>
struct swsusp_arch_regs {
struct cpu_context_save cpu_context; /* cpu context */
#ifdef CONFIG_UNICORE_FPU_F64
struct fp_state fpstate __attribute__((aligned(8)));
#endif
};
#endif
#endif /* __UNICORE_SUSPEND_H__ */
/*
* linux/arch/unicore/include/mach/pm.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 __PUV3_PM_H__
#define __PUV3_PM_H__
#include <linux/suspend.h>
struct puv3_cpu_pm_fns {
int save_count;
void (*save)(unsigned long *);
void (*restore)(unsigned long *);
int (*valid)(suspend_state_t state);
void (*enter)(suspend_state_t state);
int (*prepare)(void);
void (*finish)(void);
};
extern struct puv3_cpu_pm_fns *puv3_cpu_pm_fns;
/* sleep.S */
extern void puv3_cpu_suspend(unsigned int);
extern void puv3_cpu_resume(void);
extern int puv3_pm_enter(suspend_state_t state);
/* Defined in hibernate_asm.S */
extern int restore_image(pgd_t *resume_pg_dir, struct pbe *restore_pblist);
/* References to section boundaries */
extern const void __nosave_begin, __nosave_end;
extern struct pbe *restore_pblist;
#endif
This diff is collapsed.
/*
* linux/arch/unicore32/kernel/cpu-ucv2.c: clock scaling for the UniCore-II
*
* 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/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <mach/hardware.h>
static struct cpufreq_driver ucv2_driver;
/* make sure that only the "userspace" governor is run
* -- anything else wouldn't make sense on this platform, anyway.
*/
int ucv2_verify_speed(struct cpufreq_policy *policy)
{
if (policy->cpu)
return -EINVAL;
cpufreq_verify_within_limits(policy,
policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
return 0;
}
static unsigned int ucv2_getspeed(unsigned int cpu)
{
struct clk *mclk = clk_get(NULL, "MAIN_CLK");
if (cpu)
return 0;
return clk_get_rate(mclk)/1000;
}
static int ucv2_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int cur = ucv2_getspeed(0);
struct cpufreq_freqs freqs;
struct clk *mclk = clk_get(NULL, "MAIN_CLK");
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
if (!clk_set_rate(mclk, target_freq * 1000)) {
freqs.old = cur;
freqs.new = target_freq;
freqs.cpu = 0;
}
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return 0;
}
static int __init ucv2_cpu_init(struct cpufreq_policy *policy)
{
if (policy->cpu != 0)
return -EINVAL;
policy->cur = ucv2_getspeed(0);
policy->min = policy->cpuinfo.min_freq = 250000;
policy->max = policy->cpuinfo.max_freq = 1000000;
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
return 0;
}
static struct cpufreq_driver ucv2_driver = {
.flags = CPUFREQ_STICKY,
.verify = ucv2_verify_speed,
.target = ucv2_target,
.get = ucv2_getspeed,
.init = ucv2_cpu_init,
.name = "UniCore-II",
};
static int __init ucv2_cpufreq_init(void)
{
return cpufreq_register_driver(&ucv2_driver);
}
arch_initcall(ucv2_cpufreq_init);
/*
* 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/system.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();
}
/*
* 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
/*
* 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);
/*
* 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, =io_p2v(PKUNITY_DDR2CTRL_BASE)
@ PM BaseAddr
ldw r1, =io_p2v(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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment