blob: ba3488cad9d4fa6749e00aaf1931d59ed04f09f9 [file] [log] [blame]
/*
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc. All Rights Reserved.
*
* 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.
*/
#include <linux/linkage.h>
#include "hardware.h"
#define CCM_CBCDR 0x14
#define CCM_CBCMR 0x18
#define CCM_CSCMR1 0x1c
#define CCM_CDHIPR 0x48
#define L2_CACHE_SYNC 0x730
#define PL310_AUX_CTRL 0x104
#define PL310_DCACHE_LOCKDOWN_BASE 0x900
#define PL310_AUX_16WAY_BIT 0x10000
#define PL310_LOCKDOWN_NBREGS 8
#define PL310_LOCKDOWN_SZREG 4
#define PL310_8WAYS_MASK 0x00FF
#define PL310_16WAYS_UPPERMASK 0xFF00
#define MMDC0_MDPDC 0x4
#define MMDC0_MAPSR 0x404
#define MMDC0_MADPCR0 0x410
#define HIGH_BUS_MODE 0x0
/* Check if the cpu is cortex-a7 */
.macro is_ca7
/* Read the primary cpu number is MPIDR */
mrc p15, 0, r6, c0, c0, 0
ldr r7, =0xfff0
and r6, r6, r7
ldr r7, =0xc070
cmp r6, r7
.endm
.macro wait_for_ccm_handshake
1:
ldr r8, [r2, #CCM_CDHIPR]
cmp r8, #0
bne 1b
.endm
.macro switch_to_24MHz
/* periph2_clk2 sel to OSC_CLK */
ldr r8, [r2, #CCM_CBCMR]
orr r8, r8, #(1 << 20)
str r8, [r2, #CCM_CBCMR]
/* periph2_clk2_podf to 0 */
ldr r8, [r2, #CCM_CBCDR]
bic r8, r8, #0x7
str r8, [r2, #CCM_CBCDR]
/* periph2_clk sel to periph2_clk2 */
ldr r8, [r2, #CCM_CBCDR]
orr r8, r8, #(0x1 << 26)
str r8, [r2, #CCM_CBCDR]
wait_for_ccm_handshake
/* fabric_mmdc_podf to 0 */
ldr r8, [r2, #CCM_CBCDR]
bic r8, r8, #(0x7 << 3)
str r8, [r2, #CCM_CBCDR]
wait_for_ccm_handshake
.endm
.macro switch_to_100MHz
/* check whether periph2_clk is from top path */
ldr r8, [r2, #CCM_CBCDR]
ands r8, #(1 << 26)
beq skip_periph2_clk2_switch_100m
/* now switch periph2_clk back. */
ldr r8, [r2, #CCM_CBCDR]
bic r8, r8, #(1 << 26)
str r8, [r2, #CCM_CBCDR]
wait_for_ccm_handshake
/*
* on i.MX6SX, pre_periph2_clk will be always from
* pll2_pfd2, so no need to set pre_periph2_clk
* parent, just set the mmdc divider directly.
*/
skip_periph2_clk2_switch_100m:
/* fabric_mmdc_podf to 3 so that mmdc is 400 / 4 = 100MHz */
ldr r8, [r2, #CCM_CBCDR]
bic r8, r8, #(0x7 << 3)
orr r8, r8, #(0x3 << 3)
str r8, [r2, #CCM_CBCDR]
wait_for_ccm_handshake
.endm
.macro switch_to_400MHz
/* check whether periph2_clk is from top path */
ldr r8, [r2, #CCM_CBCDR]
ands r8, #(1 << 26)
beq skip_periph2_clk2_switch_400m
/* now switch periph2_clk back. */
ldr r8, [r2, #CCM_CBCDR]
bic r8, r8, #(1 << 26)
str r8, [r2, #CCM_CBCDR]
wait_for_ccm_handshake
/*
* on i.MX6SX, pre_periph2_clk will be always from
* pll2_pfd2, so no need to set pre_periph2_clk
* parent, just set the mmdc divider directly.
*/
skip_periph2_clk2_switch_400m:
/* fabric_mmdc_podf to 0 */
ldr r8, [r2, #CCM_CBCDR]
bic r8, r8, #(0x7 << 3)
str r8, [r2, #CCM_CBCDR]
wait_for_ccm_handshake
.endm
.macro mmdc_clk_lower_100MHz
/* if MMDC is not in 400MHz mode, skip double mu count */
cmp r1, #HIGH_BUS_MODE
bne 1f
/*
* Prior to reducing the DDR frequency (at 528/400 MHz),
* read the Measure unit count bits (MU_UNIT_DEL_NUM)
*/
ldr r8, =0x8B8
ldr r6, [r5, r8]
/* Original MU unit count */
mov r6, r6, LSR #16
ldr r4, =0x3FF
and r6, r6, r4
/* Original MU unit count * 2 */
mov r7, r6, LSL #1
/*
* Bypass the automatic measure unit when below 100 MHz
* by setting the Measure unit bypass enable bit (MU_BYP_EN)
*/
ldr r6, [r5, r8]
orr r6, r6, #0x400
str r6, [r5, r8]
/*
* Double the measure count value read in step 1 and program it in the
* measurement bypass bits (MU_BYP_VAL) of the MMDC PHY Measure Unit
* Register for the reduced frequency operation below 100 MHz
*/
ldr r6, [r5, r8]
ldr r4, =0x3FF
bic r6, r6, r4
orr r6, r6, r7
str r6, [r5, r8]
/* For freq lower than 100MHz, need to set RALAT to 2 */
ldr r6, [r5, #0x18]
bic r6, r6, #(0x7 << 6)
orr r6, r6, #(0x2 << 6)
str r6, [r5, #0x18]
1:
.endm
.macro mmdc_clk_above_100MHz
/* Make sure that the PHY measurement unit is NOT in bypass mode */
ldr r8, =0x8B8
ldr r6, [r5, r8]
bic r6, r6, #0x400
str r6, [r5, r8]
/* Now perform a Force Measurement. */
ldr r6, [r5, r8]
orr r6, r6, #0x800
str r6, [r5, r8]
/* Wait for FRC_MSR to clear. */
force_measure1:
ldr r6, [r5, r8]
and r6, r6, #0x800
cmp r6, #0x0
bne force_measure1
/* For freq higher than 100MHz, need to set RALAT to 5 */
ldr r6, [r5, #0x18]
bic r6, r6, #(0x7 << 6)
orr r6, r6, #(0x5 << 6)
str r6, [r5, #0x18]
.endm
.align 3
/*
* Below code can be used by i.MX6SX and i.MX6UL when changing the
* frequency of MMDC. the MMDC is the same on these two SOCs.
*/
ENTRY(imx6_up_lpddr2_freq_change)
push {r2 - r8}
/*
* To ensure no page table walks occur in DDR, we
* have a another page table stored in IRAM that only
* contains entries pointing to IRAM, AIPS1 and AIPS2.
* We need to set the TTBR1 to the new IRAM TLB.
* Do the following steps:
* 1. Flush the Branch Target Address Cache (BTAC)
* 2. Set TTBR1 to point to IRAM page table.
* 3. Disable page table walks in TTBR0 (PD0 = 1)
* 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0
* and 2-4G is translated by TTBR1.
*/
ldr r6, =iram_tlb_phys_addr
ldr r7, [r6]
/* Flush the Branch Target Address Cache (BTAC) */
ldr r6, =0x0
mcr p15, 0, r6, c7, c1, 6
/* Disable Branch Prediction, Z bit in SCTLR. */
mrc p15, 0, r6, c1, c0, 0
bic r6, r6, #0x800
mcr p15, 0, r6, c1, c0, 0
dsb
isb
/* Store the IRAM table in TTBR1 */
mcr p15, 0, r7, c2, c0, 1
/* Read TTBCR and set PD0=1, N = 1 */
mrc p15, 0, r6, c2, c0, 2
orr r6, r6, #0x11
mcr p15, 0, r6, c2, c0, 2
dsb
isb
/* flush the TLB */
ldr r6, =0x0
mcr p15, 0, r6, c8, c3, 0
/* Disable L1 data cache. */
mrc p15, 0, r6, c1, c0, 0
bic r6, r6, #0x4
mcr p15, 0, r6, c1, c0, 0
dsb
isb
is_ca7
beq skip_disable_l2
#ifdef CONFIG_CACHE_L2X0
/*
* Need to make sure the buffers in L2 are drained.
* Performing a sync operation does this.
*/
ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
mov r6, #0x0
str r6, [r7, #L2_CACHE_SYNC]
/*
* The second dsb might be needed to keep cache sync (device write)
* ordering with the memory accesses before it.
*/
dsb
isb
ldr r3, [r7, #PL310_AUX_CTRL]
tst r3, #PL310_AUX_16WAY_BIT
mov r3, #PL310_8WAYS_MASK
orrne r3, #PL310_16WAYS_UPPERMASK
mov r6, #PL310_LOCKDOWN_NBREGS
add r5, r7, #PL310_DCACHE_LOCKDOWN_BASE
1: /* lock Dcache and Icache */
str r3, [r5], #PL310_LOCKDOWN_SZREG
str r3, [r5], #PL310_LOCKDOWN_SZREG
subs r6, r6, #1
bne 1b
#endif
skip_disable_l2:
ldr r2, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
ldr r3, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR)
ldr r5, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
/* Disable Automatic power savings. */
ldr r6, [r5, #MMDC0_MAPSR]
orr r6, r6, #0x1
str r6, [r5, #MMDC0_MAPSR]
/* MMDC0_MDPDC disable power down timer */
ldr r6, [r5, #MMDC0_MDPDC]
bic r6, r6, #0xff00
str r6, [r5, #MMDC0_MDPDC]
/* Delay for a while */
ldr r8, =10
delay:
ldr r7, =0
cont:
ldr r6, [r5, r7]
add r7, r7, #4
cmp r7, #16
bne cont
sub r8, r8, #1
cmp r8, #0
bgt delay
/* Make the DDR explicitly enter self-refresh. */
ldr r6, [r5, #MMDC0_MAPSR]
orr r6, r6, #0x200000
str r6, [r5, #MMDC0_MAPSR]
poll_dvfs_set_1:
ldr r6, [r5, #MMDC0_MAPSR]
and r6, r6, #0x2000000
cmp r6, #0x2000000
bne poll_dvfs_set_1
/* set SBS step-by-step mode */
ldr r6, [r5, #MMDC0_MADPCR0]
orr r6, r6, #0x100
str r6, [r5, #MMDC0_MADPCR0]
ldr r6, =100000000
cmp r0, r6
bgt set_ddr_mu_above_100
mmdc_clk_lower_100MHz
set_ddr_mu_above_100:
ldr r6, =24000000
cmp r0, r6
beq set_to_24MHz
ldr r6, =100000000
cmp r0, r6
beq set_to_100MHz
switch_to_400MHz
mmdc_clk_above_100MHz
b done
set_to_24MHz:
switch_to_24MHz
b done
set_to_100MHz:
switch_to_100MHz
done:
/* clear DVFS - exit from self refresh mode */
ldr r6, [r5, #MMDC0_MAPSR]
bic r6, r6, #0x200000
str r6, [r5, #MMDC0_MAPSR]
poll_dvfs_clear_1:
ldr r6, [r5, #MMDC0_MAPSR]
and r6, r6, #0x2000000
cmp r6, #0x2000000
beq poll_dvfs_clear_1
/* Enable Automatic power savings. */
ldr r6, [r5, #MMDC0_MAPSR]
bic r6, r6, #0x1
str r6, [r5, #MMDC0_MAPSR]
ldr r6, =24000000
cmp r0, r6
beq skip_power_down
/* Enable MMDC power down timer. */
ldr r6, [r5, #MMDC0_MDPDC]
orr r6, r6, #0x5500
str r6, [r5, #MMDC0_MDPDC]
skip_power_down:
/* clear SBS - unblock DDR accesses */
ldr r6, [r5, #MMDC0_MADPCR0]
bic r6, r6, #0x100
str r6, [r5, #MMDC0_MADPCR0]
is_ca7
beq skip_enable_l2
#ifdef CONFIG_CACHE_L2X0
ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
ldr r3, [r7, #PL310_AUX_CTRL]
tst r3, #PL310_AUX_16WAY_BIT
mov r6, #PL310_LOCKDOWN_NBREGS
mov r3, #0x00 /* 8 ways mask */
orrne r3, #0x0000 /* 16 ways mask */
add r5, r7, #PL310_DCACHE_LOCKDOWN_BASE
1: /* lock Dcache and Icache */
str r3, [r5], #PL310_LOCKDOWN_SZREG
str r3, [r5], #PL310_LOCKDOWN_SZREG
subs r6, r6, #1
bne 1b
#endif
skip_enable_l2:
/* Enable L1 data cache. */
mrc p15, 0, r6, c1, c0, 0
orr r6, r6, #0x4
mcr p15, 0, r6, c1, c0, 0
/* Restore the TTBCR */
dsb
isb
/* Read TTBCR and set PD0=0, N = 0 */
mrc p15, 0, r6, c2, c0, 2
bic r6, r6, #0x11
mcr p15, 0, r6, c2, c0, 2
dsb
isb
/* flush the TLB */
ldr r6, =0x0
mcr p15, 0, r6, c8, c3, 0
dsb
isb
/* Enable Branch Prediction, Z bit in SCTLR. */
mrc p15, 0, r6, c1, c0, 0
orr r6, r6, #0x800
mcr p15, 0, r6, c1, c0, 0
/* Flush the Branch Target Address Cache (BTAC) */
ldr r6, =0x0
mcr p15, 0, r6, c7, c1, 6
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
/* Restore registers */
pop {r2 - r8}
mov pc, lr