/* * arch/arm/mach-spc300/sram.S * * Copyright (C) 2012 SPiDCOM Technologies * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include .file "sram.S" .section .sram .arm @ This is ARM code; performs the same action as .code 32 .align 2 @ Align to word boundary; "2" means the number of bits that must be zero .globl pm_process .type pm_process, %function /* Counter to repeat group of NB_INSTR instructions to reach a defined amount * of time : (((time_to_wait * current clock) / 1000000) / cpu_cycles_nb) */ #define MIU_WAIT_TIME 99 /* (((1 * 492000000) / 1000000) / 5) */ #define PLL_WAIT_TIME 2880 /* (((600 * 24000000) / 1000000) / 5) */ /* Gmac IP and MII registers and bits offsets. */ #define MDIO_REQUEST_OFFSET 0x10 #define MDIO_REQUEST_BUSY_SHIFT 0 #define MDIO_REQUEST_REG_SHIFT 6 #define MDIO_REQUEST_PHY_ADDR_SHIFT 11 /* * MDIO_REQUEST_CMD sets 3 parameters of the MDIO request : * - clock speed (to adapt to 12 Mhz) * - read request * - busy bit */ #define MDIO_REQUEST_CMD 0x25 #define MDIO_DATA_OFFSET 0x14 #define MII_STATUS_REG 1 #define MII_LINK_STATUS_SHIFT 2 /* This macro is used to clear bits in an IP register. */ .macro regbitclear, base, offset, shift, mask, tmp1, tmp2 ldr \tmp2, [\base, #\offset] mov \tmp1, #\mask mov \tmp1, \tmp1, lsl #\shift bic \tmp1, \tmp2, \tmp1 str \tmp1, [\base, #\offset] .endm /* This macro is used to set bits in an IP register. */ .macro regbitset, base, offset, shift, mask, tmp1, tmp2 ldr \tmp2, [\base, #\offset] mov \tmp1, #\mask mov \tmp1, \tmp1, lsl #\shift orr \tmp1, \tmp2, \tmp1 str \tmp1, [\base, #\offset] .endm .macro waitstatus, base, offset, statval, tmp 1: ldr \tmp, [\base, #\offset] cmp \tmp, #\statval bne 1b .endm pm_process: /* Save registers that we will use. */ push {r1, r2, r3} /* Flush TLB. */ mov r0, #0 mcr p15, 0, r0, c8, c7, 0 /* Dummy reads to fill TLB with the pages we will use after SDRAM * deactivation. This is needed to ensure that there will not be any * access to SDRAM once it is in self-refresh mode. */ ldr r0, =IO_ADDRESS(ARM_TIMER1_BASE) ldr r1, [r0, #TIMEREOIOFF_1] ldr r0, =IO_ADDRESS(MARIA_REGBANK_BASE) ldr r1, [r0] ldr r0, =IO_ADDRESS(ARM_WDT_BASE) ldr r1, [r0] ldr r0, =IO_ADDRESS(ARM_ICTL_BASE) ldr r1, [r0] ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_GMAC_VA_OFFSET ldr r1, [r0] ldr r0, [r1] ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_SPLL_ADDR_OFFSET ldr r1, [r0] ldr r0, [r1] ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_DSPPLL_ADDR_OFFSET ldr r1, [r0] ldr r0, [r1] /* Put MIU into self-refresh mode. */ ldr r0, =IO_ADDRESS(MIU_BASE) mov r1, #0x0 str r1, [r0, #0x1d0] ldr r1, =0xfffe str r1, [r0, #0x8c] ldr r1, =0xffff str r1, [r0, #0xcc] str r1, [r0, #0x10c] str r1, [r0, #0x14c] ldr r2, =MIU_WAIT_TIME 1: sub r2, r2, #1 cmp r2, #0 bne 1b ldr r1, =0x400 str r1, [r0, #0x30] ldr r1, =0x2f str r1, [r0, #0x0] ldr r1, =0x1420 str r1, [r0, #0xc] ldr r1, =0x52e str r1, [r0, #0x0] ldr r1, =0x2e str r1, [r0, #0x0] ldr r1, =0x32e str r1, [r0, #0x0] ldr r1, =0x2e str r1, [r0, #0x0] ldr r2, =MIU_WAIT_TIME 1: sub r2, r2, #1 cmp r2, #0 bne 1b ldr r1, =0xffff str r1, [r0, #0x8c] ldr r1, =0x202e str r1, [r0, #0x0] ldr r1, =0xe2a2 str r1, [r0, #0x4] /* * Bypass PLLs. We keep Peripheral PLL alive for ETH PHY and GMAC. */ ldr r0, =IO_ADDRESS(MARIA_REGBANK_BASE) ldr r1, =PLL_CMD_BYPASS /* System PLL */ str r1, [r0, #RB_SPLL_BYPASS_OFFSET] waitstatus r0, RB_SPLL_BYPASS_STAT_OFFSET, PLL_IS_BYPASS, r2 /* DSP PLL */ str r1, [r0, #RB_DPLL_BYPASS_OFFSET] waitstatus r0, RB_DPLL_BYPASS_STAT_OFFSET, PLL_IS_BYPASS, r2 /* * Switch OFF PLLs. */ /* System PLL */ ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_SPLL_ADDR_OFFSET ldr r0, [r1] regbitset r0, SPLL_CTRL_OFFSET, MSEPLL_SPPLL_CTRL_PD_SHIFT, \ MSEPLL_SPPLL_CTRL_PD_MASK, r1, r2 /* DSP PLL */ ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_DSPPLL_ADDR_OFFSET ldr r0, [r1] regbitset r0, DSPPLL_CONF_OFFSET, MSEAFE_DPLL_CONF_PD_SHIFT, \ MSEAFE_DPLL_CONF_PD_MASK, r1, r2 regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_CLK_SHIFT, \ MSEAFE_DPLL_CTRL_PD_CLK_MASK, r1, r2 regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_SHIFT, \ MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_MASK, r1, r2 regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_SHIFT, \ MSEAFE_DPLL_CTRL_PD_DAC_CLK_MASK, r1, r2 regbitset r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_ADC_CLK_SHIFT, \ MSEAFE_DPLL_CTRL_PD_ADC_CLK_MASK, r1, r2 /* Set WDT timer to 1.4s (at 12MHz) */ mov r1, #8 ldr r0, =IO_ADDRESS(ARM_WDT_BASE) + WDTTimeoutRangeReg_Offset /* Save old value of the timeout */ ldr r3, [r0] str r1, [r0] 2: /* Refresh WDT. */ ldr r0, =IO_ADDRESS(ARM_WDT_BASE) + WDTCounterResetReg_Offset mov r1, #WDT_CR_VAL str r1, [r0] /* WDT IP runs twice slower than ARM, and need a few cycles to * clear the IRQ line in GIC, so we need to wait to make sure that it * will be cleared before entering wait for interrupt mode. */ nop nop nop nop /* Check link status. We need to read the status register twice on some * PHYs. */ ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_GMAC_VA_OFFSET ldr r1, [r0] add r0, r1, #MDIO_REQUEST_OFFSET ldr r2, =SRAM_DATA_BASE_VA + SRAM_DATA_PHY_ADDR_OFFSET ldr r1, [r2] lsl r1, r1, #MDIO_REQUEST_PHY_ADDR_SHIFT add r1, r1, #(MII_STATUS_REG << MDIO_REQUEST_REG_SHIFT \ + MDIO_REQUEST_CMD) str r1, [r0] 1: ldr r1, [r0] tst r1, #(1 << MDIO_REQUEST_BUSY_SHIFT) bne 1b ldr r1, [r2] lsl r1, r1, #MDIO_REQUEST_PHY_ADDR_SHIFT add r1, r1, #(MII_STATUS_REG << MDIO_REQUEST_REG_SHIFT \ + MDIO_REQUEST_CMD) str r1, [r0] 1: ldr r1, [r0] tst r1, #(1 << MDIO_REQUEST_BUSY_SHIFT) bne 1b ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_GMAC_VA_OFFSET ldr r1, [r0] add r0, r1, #MDIO_DATA_OFFSET ldr r1, [r0] tst r1, #(1 << MII_LINK_STATUS_SHIFT) bne exit_sram wait_for_irq: /* Put ARM in wait for interrupt mode. */ mov r0, #0 mcr p15, 0, r0, c7, c0, 4 /* Test if we are awaken by timer tick. If so, we need to inc the counter in sram */ ldr r0, =IO_ADDRESS(ARM_ICTL_BASE) + IRQ_FINALSTATUS_OFFSET_L ldr r1, [r0] tst r1, #INTMASK_TIMER_1 beq 1f /* Inc tick count */ ldr r0, =SRAM_DATA_BASE_VA + SRAM_DATA_TICK_CNT_OFFSET ldr r1, [r0] add r1, r1, #1 str r1, [r0] /* Clear IRQ */ ldr r0, =IO_ADDRESS(ARM_TIMER1_BASE) ldr r1, [r0, #TIMEREOIOFF_1] nop nop /* Go back in wait for IRQ */ b wait_for_irq 1: /* Test if we are awaken by WDT. If so, we need to refresh it and go back to wait for interrupt mode. */ ldr r0, =IO_ADDRESS(ARM_ICTL_BASE) + IRQ_FINALSTATUS_OFFSET_L ldr r1, [r0] tst r1, #INTMASK_WDT bne 2b exit_sram: /* Restore WDT timeout value */ ldr r0, =IO_ADDRESS(ARM_WDT_BASE) + WDTTimeoutRangeReg_Offset /* Save old value of the timeout */ str r3, [r0] /* * Switch ON PLLs and wait until they stabilize */ /* System PLL */ ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_SPLL_ADDR_OFFSET ldr r0, [r1] regbitclear r0, SPLL_CTRL_OFFSET, MSEPLL_SPPLL_CTRL_PD_SHIFT, \ MSEPLL_SPPLL_CTRL_PD_MASK, r1, r2 /* DSP PLL */ ldr r1, =SRAM_DATA_BASE_VA + SRAM_DATA_DSPPLL_ADDR_OFFSET ldr r0, [r1] regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_ADC_CLK_SHIFT, \ MSEAFE_DPLL_CTRL_PD_ADC_CLK_MASK, r1, r2 regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_SHIFT, \ MSEAFE_DPLL_CTRL_PD_DAC_CLK_MASK, r1, r2 regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_SHIFT, \ MSEAFE_DPLL_CTRL_PD_DAC_CLK_OUT_MASK, r1, r2 regbitclear r0, DSPPLL_CTRL_OFFSET, MSEAFE_DPLL_CTRL_PD_CLK_SHIFT, \ MSEAFE_DPLL_CTRL_PD_CLK_MASK, r1, r2 regbitclear r0, DSPPLL_CONF_OFFSET, MSEAFE_DPLL_CONF_PD_SHIFT, \ MSEAFE_DPLL_CONF_PD_MASK, r1, r2 /* Active wait */ ldr r2, =PLL_WAIT_TIME 1: sub r2, r2, #1 cmp r2, #0 bne 1b /* * Switch to PLL clock */ ldr r0, =IO_ADDRESS(MARIA_REGBANK_BASE) ldr r1, =PLL_CMD_PLL /* System PLL */ str r1, [r0, #RB_SPLL_BYPASS_OFFSET] waitstatus r0, RB_SPLL_BYPASS_STAT_OFFSET, PLL_IS_PLL, r2 /* DSP PLL */ str r1, [r0, #RB_DPLL_BYPASS_OFFSET] waitstatus r0, RB_DPLL_BYPASS_STAT_OFFSET, PLL_IS_PLL, r2 /* Put MIU back into auto-refresh mode. */ ldr r0, =IO_ADDRESS(MIU_BASE) ldr r1, =0x2a2 str r1, [r0, #0x4] ldr r1, =0x2e str r1, [r0, #0x0] ldr r1, =0xfffe str r1, [r0, #0x8c] ldr r2, =MIU_WAIT_TIME 1: sub r2, r2, #1 cmp r2, #0 bne 1b ldr r1, =0x32e str r1, [r0, #0x0] ldr r1, =0x2e str r1, [r0, #0x0] ldr r1, =0xe str r1, [r0, #0x0] ldr r2, =MIU_WAIT_TIME 1: sub r2, r2, #1 cmp r2, #0 bne 1b ldr r1, =0x420 str r1, [r0, #0xc] ldr r1, =0x0 str r1, [r0, #0x8c] str r1, [r0, #0xcc] str r1, [r0, #0x10c] str r1, [r0, #0x14c] /* Restore registers. */ pop {r1, r2, r3} /* Back to my caller. */ mov pc, lr .size pm_process, . - pm_process