| /* sleep.S: power saving mode entry | |
| * | |
| * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | |
| * Written by David Woodhouse (dwmw2@infradead.org) | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public License | |
| * as published by the Free Software Foundation; either version | |
| * 2 of the License, or (at your option) any later version. | |
| * | |
| */ | |
| #include <linux/sys.h> | |
| #include <linux/linkage.h> | |
| #include <asm/setup.h> | |
| #include <asm/segment.h> | |
| #include <asm/page.h> | |
| #include <asm/ptrace.h> | |
| #include <asm/errno.h> | |
| #include <asm/cache.h> | |
| #include <asm/spr-regs.h> | |
| #define __addr_MASK 0xfeff9820 /* interrupt controller mask */ | |
| #define __addr_FR55X_DRCN 0xfeff0218 /* Address of DRCN register */ | |
| #define FR55X_DSTS_OFFSET -4 /* Offset from DRCN to DSTS */ | |
| #define FR55X_SDRAMC_DSTS_SSI 0x00000002 /* indicates that the SDRAM is in self-refresh mode */ | |
| #define __addr_FR4XX_DRCN 0xfe000430 /* Address of DRCN register */ | |
| #define FR4XX_DSTS_OFFSET -8 /* Offset from DRCN to DSTS */ | |
| #define FR4XX_SDRAMC_DSTS_SSI 0x00000001 /* indicates that the SDRAM is in self-refresh mode */ | |
| #define SDRAMC_DRCN_SR 0x00000001 /* transition SDRAM into self-refresh mode */ | |
| .section .bss | |
| .balign 8 | |
| .globl __sleep_save_area | |
| __sleep_save_area: | |
| .space 16 | |
| .text | |
| .balign 4 | |
| .macro li v r | |
| sethi.p %hi(\v),\r | |
| setlo %lo(\v),\r | |
| .endm | |
| #ifdef CONFIG_PM | |
| ############################################################################### | |
| # | |
| # CPU suspension routine | |
| # - void frv_cpu_suspend(unsigned long pdm_mode) | |
| # | |
| ############################################################################### | |
| .globl frv_cpu_suspend | |
| .type frv_cpu_suspend,@function | |
| frv_cpu_suspend: | |
| #---------------------------------------------------- | |
| # save hsr0, psr, isr, and lr for resume code | |
| #---------------------------------------------------- | |
| li __sleep_save_area,gr11 | |
| movsg hsr0,gr4 | |
| movsg psr,gr5 | |
| movsg isr,gr6 | |
| movsg lr,gr7 | |
| stdi gr4,@(gr11,#0) | |
| stdi gr6,@(gr11,#8) | |
| # store the return address from sleep in GR14, and its complement in GR13 as a check | |
| li __ramboot_resume,gr14 | |
| #ifdef CONFIG_MMU | |
| # Resume via RAMBOOT# will turn MMU off, so bootloader needs a physical address. | |
| sethi.p %hi(__page_offset),gr13 | |
| setlo %lo(__page_offset),gr13 | |
| sub gr14,gr13,gr14 | |
| #endif | |
| not gr14,gr13 | |
| #---------------------------------------------------- | |
| # preload and lock into icache that code which may have to run | |
| # when dram is in self-refresh state. | |
| #---------------------------------------------------- | |
| movsg hsr0, gr3 | |
| li HSR0_ICE,gr4 | |
| or gr3,gr4,gr3 | |
| movgs gr3,hsr0 | |
| or gr3,gr8,gr7 // add the sleep bits for later | |
| li #__icache_lock_start,gr3 | |
| li #__icache_lock_end,gr4 | |
| 1: icpl gr3,gr0,#1 | |
| addi gr3,#L1_CACHE_BYTES,gr3 | |
| cmp gr4,gr3,icc0 | |
| bhi icc0,#0,1b | |
| # disable exceptions | |
| movsg psr,gr8 | |
| andi.p gr8,#~PSR_PIL,gr8 | |
| andi gr8,~PSR_ET,gr8 | |
| movgs gr8,psr | |
| ori gr8,#PSR_ET,gr8 | |
| srli gr8,#28,gr4 | |
| subicc gr4,#3,gr0,icc0 | |
| beq icc0,#0,1f | |
| # FR4xx | |
| li __addr_FR4XX_DRCN,gr4 | |
| li FR4XX_SDRAMC_DSTS_SSI,gr5 | |
| li FR4XX_DSTS_OFFSET,gr6 | |
| bra __icache_lock_start | |
| 1: | |
| # FR5xx | |
| li __addr_FR55X_DRCN,gr4 | |
| li FR55X_SDRAMC_DSTS_SSI,gr5 | |
| li FR55X_DSTS_OFFSET,gr6 | |
| bra __icache_lock_start | |
| .size frv_cpu_suspend, .-frv_cpu_suspend | |
| # | |
| # the final part of the sleep sequence... | |
| # - we want it to be be cacheline aligned so we can lock it into the icache easily | |
| # On entry: gr7 holds desired hsr0 sleep value | |
| # gr8 holds desired psr sleep value | |
| # | |
| .balign L1_CACHE_BYTES | |
| .type __icache_lock_start,@function | |
| __icache_lock_start: | |
| #---------------------------------------------------- | |
| # put SDRAM in self-refresh mode | |
| #---------------------------------------------------- | |
| # Flush all data in the cache using the DCEF instruction. | |
| dcef @(gr0,gr0),#1 | |
| # Stop DMAC transfer | |
| # Execute dummy load from SDRAM | |
| ldi @(gr11,#0),gr11 | |
| # put the SDRAM into self-refresh mode | |
| ld @(gr4,gr0),gr11 | |
| ori gr11,#SDRAMC_DRCN_SR,gr11 | |
| st gr11,@(gr4,gr0) | |
| membar | |
| # wait for SDRAM to reach self-refresh mode | |
| 1: ld @(gr4,gr6),gr11 | |
| andcc gr11,gr5,gr11,icc0 | |
| beq icc0,#0,1b | |
| # Set the GPIO register so that the IRQ[3:0] pins become valid, as required. | |
| # Set the clock mode (CLKC register) as required. | |
| # - At this time, also set the CLKC register P0 bit. | |
| # Set the HSR0 register PDM field. | |
| movgs gr7,hsr0 | |
| # Execute NOP 32 times. | |
| .rept 32 | |
| nop | |
| .endr | |
| #if 0 // Fujitsu recommend to skip this and will update docs. | |
| # Release the interrupt mask setting of the MASK register of the | |
| # interrupt controller if necessary. | |
| sti gr10,@(gr9,#0) | |
| membar | |
| #endif | |
| # Set the PSR register ET bit to 1 to enable interrupts. | |
| movgs gr8,psr | |
| ################################################### | |
| # this is only reached if waking up via interrupt | |
| ################################################### | |
| # Execute NOP 32 times. | |
| .rept 32 | |
| nop | |
| .endr | |
| #---------------------------------------------------- | |
| # wake SDRAM from self-refresh mode | |
| #---------------------------------------------------- | |
| ld @(gr4,gr0),gr11 | |
| andi gr11,#~SDRAMC_DRCN_SR,gr11 | |
| st gr11,@(gr4,gr0) | |
| membar | |
| 2: | |
| ld @(gr4,gr6),gr11 // Wait for it to come back... | |
| andcc gr11,gr5,gr0,icc0 | |
| bne icc0,0,2b | |
| # wait for the SDRAM to stabilise | |
| li 0x0100000,gr3 | |
| 3: subicc gr3,#1,gr3,icc0 | |
| bne icc0,#0,3b | |
| # now that DRAM is back, this is the end of the code which gets | |
| # locked in icache. | |
| __icache_lock_end: | |
| .size __icache_lock_start, .-__icache_lock_start | |
| # Fall-through to the RAMBOOT# wakeup path | |
| ############################################################################### | |
| # | |
| # resume from suspend re-entry point reached via RAMBOOT# and bootloader | |
| # | |
| ############################################################################### | |
| __ramboot_resume: | |
| #---------------------------------------------------- | |
| # restore hsr0, psr, isr, and leave saved lr in gr7 | |
| #---------------------------------------------------- | |
| li __sleep_save_area,gr11 | |
| #ifdef CONFIG_MMU | |
| movsg hsr0,gr4 | |
| sethi.p %hi(HSR0_EXMMU),gr3 | |
| setlo %lo(HSR0_EXMMU),gr3 | |
| andcc gr3,gr4,gr0,icc0 | |
| bne icc0,#0,2f | |
| # need to use physical address | |
| sethi.p %hi(__page_offset),gr3 | |
| setlo %lo(__page_offset),gr3 | |
| sub gr11,gr3,gr11 | |
| # flush all tlb entries | |
| setlos #64,gr4 | |
| setlos.p #PAGE_SIZE,gr5 | |
| setlos #0,gr6 | |
| 1: | |
| tlbpr gr6,gr0,#6,#0 | |
| subicc.p gr4,#1,gr4,icc0 | |
| add gr6,gr5,gr6 | |
| bne icc0,#2,1b | |
| # need a temporary mapping for the current physical address we are | |
| # using between time MMU is enabled and jump to virtual address is | |
| # made. | |
| sethi.p %hi(0x00000000),gr4 | |
| setlo %lo(0x00000000),gr4 ; physical address | |
| setlos #xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr5 | |
| or gr4,gr5,gr5 | |
| movsg cxnr,gr13 | |
| or gr4,gr13,gr4 | |
| movgs gr4,iamlr1 ; mapped from real address 0 | |
| movgs gr5,iampr1 ; cached kernel memory at 0x00000000 | |
| 2: | |
| #endif | |
| lddi @(gr11,#0),gr4 ; hsr0, psr | |
| lddi @(gr11,#8),gr6 ; isr, lr | |
| movgs gr4,hsr0 | |
| bar | |
| #ifdef CONFIG_MMU | |
| sethi.p %hi(1f),gr11 | |
| setlo %lo(1f),gr11 | |
| jmpl @(gr11,gr0) | |
| 1: | |
| movgs gr0,iampr1 ; get rid of temporary mapping | |
| #endif | |
| movgs gr5,psr | |
| movgs gr6,isr | |
| #---------------------------------------------------- | |
| # unlock the icache which was locked before going to sleep | |
| #---------------------------------------------------- | |
| li __icache_lock_start,gr3 | |
| li __icache_lock_end,gr4 | |
| 1: icul gr3 | |
| addi gr3,#L1_CACHE_BYTES,gr3 | |
| cmp gr4,gr3,icc0 | |
| bhi icc0,#0,1b | |
| #---------------------------------------------------- | |
| # back to business as usual | |
| #---------------------------------------------------- | |
| jmpl @(gr7,gr0) ; | |
| #endif /* CONFIG_PM */ | |
| ############################################################################### | |
| # | |
| # CPU core sleep mode routine | |
| # | |
| ############################################################################### | |
| .globl frv_cpu_core_sleep | |
| .type frv_cpu_core_sleep,@function | |
| frv_cpu_core_sleep: | |
| # Preload into icache. | |
| li #__core_sleep_icache_lock_start,gr3 | |
| li #__core_sleep_icache_lock_end,gr4 | |
| 1: icpl gr3,gr0,#1 | |
| addi gr3,#L1_CACHE_BYTES,gr3 | |
| cmp gr4,gr3,icc0 | |
| bhi icc0,#0,1b | |
| bra __core_sleep_icache_lock_start | |
| .balign L1_CACHE_BYTES | |
| __core_sleep_icache_lock_start: | |
| # (1) Set the PSR register ET bit to 0 to disable interrupts. | |
| movsg psr,gr8 | |
| andi.p gr8,#~(PSR_PIL),gr8 | |
| andi gr8,#~(PSR_ET),gr4 | |
| movgs gr4,psr | |
| #if 0 // Fujitsu recommend to skip this and will update docs. | |
| # (2) Set '1' to all bits in the MASK register of the interrupt | |
| # controller and mask interrupts. | |
| sethi.p %hi(__addr_MASK),gr9 | |
| setlo %lo(__addr_MASK),gr9 | |
| sethi.p %hi(0xffff0000),gr4 | |
| setlo %lo(0xffff0000),gr4 | |
| ldi @(gr9,#0),gr10 | |
| sti gr4,@(gr9,#0) | |
| #endif | |
| # (3) Flush all data in the cache using the DCEF instruction. | |
| dcef @(gr0,gr0),#1 | |
| # (4) Execute the memory barrier instruction | |
| membar | |
| # (5) Set the GPIO register so that the IRQ[3:0] pins become valid, as required. | |
| # (6) Set the clock mode (CLKC register) as required. | |
| # - At this time, also set the CLKC register P0 bit. | |
| # (7) Set the HSR0 register PDM field to 001 . | |
| movsg hsr0,gr4 | |
| ori gr4,HSR0_PDM_CORE_SLEEP,gr4 | |
| movgs gr4,hsr0 | |
| # (8) Execute NOP 32 times. | |
| .rept 32 | |
| nop | |
| .endr | |
| #if 0 // Fujitsu recommend to skip this and will update docs. | |
| # (9) Release the interrupt mask setting of the MASK register of the | |
| # interrupt controller if necessary. | |
| sti gr10,@(gr9,#0) | |
| membar | |
| #endif | |
| # (10) Set the PSR register ET bit to 1 to enable interrupts. | |
| movgs gr8,psr | |
| __core_sleep_icache_lock_end: | |
| # Unlock from icache | |
| li __core_sleep_icache_lock_start,gr3 | |
| li __core_sleep_icache_lock_end,gr4 | |
| 1: icul gr3 | |
| addi gr3,#L1_CACHE_BYTES,gr3 | |
| cmp gr4,gr3,icc0 | |
| bhi icc0,#0,1b | |
| bralr | |
| .size frv_cpu_core_sleep, .-frv_cpu_core_sleep |