Merge branch 'arch/next' into next
* arch/next: (81 commits) MLK-21599-1 arm64: Kconfig: Make FORCE_MAX_ZONEORDER configurable LF-171 ARM: imx: Add cpu type check for imx6ulz in msl code LF-176 ARM: imx: mach-imx6q: Revert "ARM: imx: correct the enet_clk_ref clock string" drivers: soc: fsl: add qixis driver Add APIs to setup HugeTLB mappings for USDPAA ...5.4-rM2-2.2.x-imx-squashed
commit
96aa3bc2ff
|
@ -85,6 +85,22 @@ extern void __bad_udelay(void);
|
|||
__const_udelay((n) * UDELAY_MULT)) : \
|
||||
__udelay(n))
|
||||
|
||||
#define spin_event_timeout(condition, timeout, delay) \
|
||||
({ \
|
||||
typeof(condition) __ret; \
|
||||
int i = 0; \
|
||||
while (!(__ret = (condition)) && (i++ < timeout)) { \
|
||||
if (delay) \
|
||||
udelay(delay); \
|
||||
else \
|
||||
cpu_relax(); \
|
||||
udelay(1); \
|
||||
} \
|
||||
if (!__ret) \
|
||||
__ret = (condition); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
/* Loop-based definitions for assembly code. */
|
||||
extern void __loop_delay(unsigned long loops);
|
||||
extern void __loop_udelay(unsigned long usecs);
|
||||
|
|
|
@ -123,6 +123,7 @@ static inline u32 __raw_readl(const volatile void __iomem *addr)
|
|||
#define MT_DEVICE_NONSHARED 1
|
||||
#define MT_DEVICE_CACHED 2
|
||||
#define MT_DEVICE_WC 3
|
||||
#define MT_MEMORY_RW_NS 4
|
||||
/*
|
||||
* types 4 onwards can be found in asm/mach/map.h and are undefined
|
||||
* for ioremap
|
||||
|
@ -224,6 +225,34 @@ void __iomem *pci_remap_cfgspace(resource_size_t res_cookie, size_t size);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
/* access ports */
|
||||
#define setbits32(_addr, _v) iowrite32be(ioread32be(_addr) | (_v), (_addr))
|
||||
#define clrbits32(_addr, _v) iowrite32be(ioread32be(_addr) & ~(_v), (_addr))
|
||||
|
||||
#define setbits16(_addr, _v) iowrite16be(ioread16be(_addr) | (_v), (_addr))
|
||||
#define clrbits16(_addr, _v) iowrite16be(ioread16be(_addr) & ~(_v), (_addr))
|
||||
|
||||
#define setbits8(_addr, _v) iowrite8(ioread8(_addr) | (_v), (_addr))
|
||||
#define clrbits8(_addr, _v) iowrite8(ioread8(_addr) & ~(_v), (_addr))
|
||||
|
||||
/* Clear and set bits in one shot. These macros can be used to clear and
|
||||
* set multiple bits in a register using a single read-modify-write. These
|
||||
* macros can also be used to set a multiple-bit bit pattern using a mask,
|
||||
* by specifying the mask in the 'clear' parameter and the new bit pattern
|
||||
* in the 'set' parameter.
|
||||
*/
|
||||
|
||||
#define clrsetbits_be32(addr, clear, set) \
|
||||
iowrite32be((ioread32be(addr) & ~(clear)) | (set), (addr))
|
||||
#define clrsetbits_le32(addr, clear, set) \
|
||||
iowrite32le((ioread32le(addr) & ~(clear)) | (set), (addr))
|
||||
#define clrsetbits_be16(addr, clear, set) \
|
||||
iowrite16be((ioread16be(addr) & ~(clear)) | (set), (addr))
|
||||
#define clrsetbits_le16(addr, clear, set) \
|
||||
iowrite16le((ioread16le(addr) & ~(clear)) | (set), (addr))
|
||||
#define clrsetbits_8(addr, clear, set) \
|
||||
iowrite8((ioread8(addr) & ~(clear)) | (set), (addr))
|
||||
|
||||
/*
|
||||
* IO port access primitives
|
||||
* -------------------------
|
||||
|
@ -410,6 +439,8 @@ void __iomem *ioremap_wc(resource_size_t res_cookie, size_t size);
|
|||
#define ioremap_wc ioremap_wc
|
||||
#define ioremap_wt ioremap_wc
|
||||
|
||||
void __iomem *ioremap_cache_ns(resource_size_t res_cookie, size_t size);
|
||||
|
||||
void iounmap(volatile void __iomem *iomem_cookie);
|
||||
#define iounmap iounmap
|
||||
|
||||
|
|
|
@ -18,9 +18,9 @@ struct map_desc {
|
|||
unsigned int type;
|
||||
};
|
||||
|
||||
/* types 0-3 are defined in asm/io.h */
|
||||
/* types 0-4 are defined in asm/io.h */
|
||||
enum {
|
||||
MT_UNCACHED = 4,
|
||||
MT_UNCACHED = 5,
|
||||
MT_CACHECLEAN,
|
||||
MT_MINICLEAN,
|
||||
MT_LOW_VECTORS,
|
||||
|
|
|
@ -116,6 +116,13 @@ extern pgprot_t pgprot_s2_device;
|
|||
#define pgprot_noncached(prot) \
|
||||
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_UNCACHED)
|
||||
|
||||
#define pgprot_cached(prot) \
|
||||
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_CACHED)
|
||||
|
||||
#define pgprot_cached_ns(prot) \
|
||||
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_DEV_CACHED | \
|
||||
L_PTE_MT_DEV_NONSHARED)
|
||||
|
||||
#define pgprot_writecombine(prot) \
|
||||
__pgprot_modify(prot, L_PTE_MT_MASK, L_PTE_MT_BUFFERABLE)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
* reading the RTC at bootup, etc...
|
||||
*/
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/export.h>
|
||||
|
@ -107,5 +108,7 @@ void __init time_init(void)
|
|||
of_clk_init(NULL);
|
||||
#endif
|
||||
timer_probe();
|
||||
|
||||
tick_setup_hrtimer_broadcast();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,9 +40,29 @@ config HAVE_IMX_GPC
|
|||
bool
|
||||
select PM_GENERIC_DOMAINS if PM
|
||||
|
||||
config HAVE_IMX_GPCV2
|
||||
bool
|
||||
select PM_GENERIC_DOMAINS if PM
|
||||
|
||||
config HAVE_IMX_MMDC
|
||||
bool
|
||||
|
||||
config HAVE_IMX_AMP
|
||||
bool
|
||||
|
||||
config HAVE_IMX_DDRC
|
||||
bool
|
||||
select HAVE_IMX_BUSFREQ
|
||||
|
||||
config HAVE_IMX_BUSFREQ
|
||||
bool
|
||||
|
||||
config HAVE_IMX_MU
|
||||
bool
|
||||
|
||||
config HAVE_IMX_RPMSG
|
||||
bool
|
||||
|
||||
config HAVE_IMX_SRC
|
||||
def_bool y if SMP
|
||||
select ARCH_HAS_RESET_CONTROLLER
|
||||
|
@ -511,7 +531,12 @@ config SOC_IMX6SLL
|
|||
config SOC_IMX6SX
|
||||
bool "i.MX6 SoloX support"
|
||||
select PINCTRL_IMX6SX
|
||||
select HAVE_IMX_AMP
|
||||
select SOC_IMX6
|
||||
select HAVE_IMX_MU
|
||||
select HAVE_IMX_RPMSG
|
||||
select IMX_SEMA4
|
||||
select KEYBOARD_SNVS_PWRKEY
|
||||
|
||||
help
|
||||
This enables support for Freescale i.MX6 SoloX processor.
|
||||
|
@ -546,6 +571,11 @@ config SOC_IMX7D_CA7
|
|||
select HAVE_IMX_MMDC
|
||||
select HAVE_IMX_SRC
|
||||
select IMX_GPCV2
|
||||
select HAVE_IMX_DDRC
|
||||
select HAVE_IMX_MU
|
||||
select HAVE_IMX_RPMSG
|
||||
select HAVE_IMX_GPCV2
|
||||
select KEYBOARD_SNVS_PWRKEY
|
||||
|
||||
config SOC_IMX7D_CM4
|
||||
bool
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y := cpu.o system.o irq-common.o
|
||||
obj-y := cpu.o system.o irq-common.o common.o
|
||||
|
||||
obj-$(CONFIG_SOC_IMX21) += mm-imx21.o
|
||||
|
||||
|
@ -25,11 +25,18 @@ obj-$(CONFIG_MXC_DEBUG_BOARD) += 3ds_debugboard.o
|
|||
ifeq ($(CONFIG_CPU_IDLE),y)
|
||||
obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
|
||||
obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
|
||||
obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o
|
||||
obj-$(CONFIG_SOC_IMX6SLL) += cpuidle-imx6sx.o
|
||||
obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6sx.o
|
||||
obj-$(CONFIG_SOC_IMX6UL) += cpuidle-imx6sx.o
|
||||
obj-$(CONFIG_SOC_IMX7ULP) += cpuidle-imx7ulp.o
|
||||
AFLAGS_imx6sl_low_power_idle.o :=-Wa,-march=armv7-a
|
||||
obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o imx6sl_low_power_idle.o
|
||||
AFLAGS_imx6sll_low_power_idle.o :=-Wa,-march=armv7-a
|
||||
obj-$(CONFIG_SOC_IMX6SLL) += cpuidle-imx6sll.o imx6sll_low_power_idle.o
|
||||
obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6sx.o imx6sx_low_power_idle.o
|
||||
AFLAGS_imx6sx_low_power_idle.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_imx6ul_low_power_idle.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_imx6ull_low_power_idle.o :=-Wa,-march=armv7-a
|
||||
obj-$(CONFIG_SOC_IMX6UL) += cpuidle-imx6ul.o imx6ul_low_power_idle.o imx6ull_low_power_idle.o
|
||||
obj-$(CONFIG_SOC_IMX7ULP) += cpuidle-imx7ulp.o pm-rpmsg.o
|
||||
AFLAGS_imx7d_low_power_idle.o :=-Wa,-march=armv7-a
|
||||
obj-$(CONFIG_SOC_IMX7D_CA7) += cpuidle-imx7d.o imx7d_low_power_idle.o
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SND_SOC_IMX_PCM_FIQ
|
||||
|
@ -70,26 +77,46 @@ obj-$(CONFIG_MACH_IMX35_DT) += imx35-dt.o
|
|||
|
||||
obj-$(CONFIG_HAVE_IMX_ANATOP) += anatop.o
|
||||
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
|
||||
obj-$(CONFIG_HAVE_IMX_GPCV2) += gpcv2.o
|
||||
obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
|
||||
obj-$(CONFIG_HAVE_IMX_SRC) += src.o
|
||||
obj-$(CONFIG_HAVE_IMX_DDRC) += ddrc.o
|
||||
obj-$(CONFIG_HAVE_IMX_MU) += mu.o
|
||||
ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),)
|
||||
AFLAGS_headsmp.o :=-Wa,-march=armv7-a
|
||||
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
endif
|
||||
obj-$(CONFIG_SOC_IMX6Q) += mach-imx6q.o
|
||||
obj-$(CONFIG_SOC_IMX6SL) += mach-imx6sl.o
|
||||
obj-$(CONFIG_SOC_IMX6SLL) += mach-imx6sl.o
|
||||
obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o
|
||||
obj-$(CONFIG_SOC_IMX6Q) += mach-imx6q.o ddr3_freq_imx6.o smp_wfe_imx6.o \
|
||||
lpddr2_freq_imx6q.o
|
||||
obj-$(CONFIG_SOC_IMX6SL) += mach-imx6sl.o lpddr2_freq_imx6.o
|
||||
obj-$(CONFIG_SOC_IMX6SLL) += mach-imx6sl.o lpddr2_freq_imx6sll.o
|
||||
obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o ddr3_freq_imx6sx.o smp_wfe_imx6.o lpddr2_freq_imx6sx.o
|
||||
obj-$(CONFIG_SOC_IMX6UL) += mach-imx6ul.o
|
||||
obj-$(CONFIG_SOC_IMX7D_CA7) += mach-imx7d.o
|
||||
obj-$(CONFIG_SOC_IMX7D_CA7) += mach-imx7d.o pm-imx7.o ddr3_freq_imx7d.o smp_wfe.o \
|
||||
lpddr3_freq_imx.o suspend-imx7.o
|
||||
obj-$(CONFIG_SOC_IMX7D_CM4) += mach-imx7d-cm4.o
|
||||
obj-$(CONFIG_SOC_IMX7ULP) += mach-imx7ulp.o pm-imx7ulp.o
|
||||
|
||||
obj-y += busfreq-imx.o busfreq_ddr3.o busfreq_lpddr2.o
|
||||
AFLAGS_smp_wfe.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_smp_wfe_imx6.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_ddr3_freq_imx7d.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_lpddr3_freq_imx.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_ddr3_freq_imx6.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_lpddr2_freq_imx6.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_lpddr2_freq_imx6q.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_lpddr2_freq_imx6sx.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_lpddr2_freq_imx6sll.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_ddr3_freq_imx6sx.o :=-Wa,-march=armv7-a
|
||||
|
||||
ifeq ($(CONFIG_SUSPEND),y)
|
||||
AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_suspend-imx7.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_suspend-imx7ulp.o :=-Wa,-march=armv7-a
|
||||
obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
|
||||
obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
|
||||
obj-$(CONFIG_SOC_IMX7ULP) += suspend-imx7ulp.o
|
||||
endif
|
||||
obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright 2017-2018 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -16,38 +17,63 @@
|
|||
#define REG_SET 0x4
|
||||
#define REG_CLR 0x8
|
||||
|
||||
#define ANADIG_ARM_PLL 0x60
|
||||
#define ANADIG_DDR_PLL 0x70
|
||||
#define ANADIG_SYS_PLL 0xb0
|
||||
#define ANADIG_ENET_PLL 0xe0
|
||||
#define ANADIG_AUDIO_PLL 0xf0
|
||||
#define ANADIG_VIDEO_PLL 0x130
|
||||
|
||||
#define ANADIG_REG_2P5 0x130
|
||||
#define ANADIG_REG_CORE 0x140
|
||||
#define ANADIG_ANA_MISC0 0x150
|
||||
#define ANADIG_USB1_CHRG_DETECT 0x1b0
|
||||
#define ANADIG_USB2_CHRG_DETECT 0x210
|
||||
#define ANADIG_ANA_MISC2 0x170
|
||||
#define ANADIG_DIGPROG 0x260
|
||||
#define ANADIG_DIGPROG_IMX6SL 0x280
|
||||
#define ANADIG_DIGPROG_IMX7D 0x800
|
||||
|
||||
#define SRC_SBMR2 0x1c
|
||||
|
||||
#define BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG 0x40000
|
||||
#define BM_ANADIG_REG_2P5_ENABLE_PULLDOWN 0x8
|
||||
#define BM_ANADIG_REG_CORE_FET_ODRIVE 0x20000000
|
||||
#define BM_ANADIG_REG_CORE_REG1 (0x1f << 9)
|
||||
#define BM_ANADIG_REG_CORE_REG2 (0x1f << 18)
|
||||
#define BP_ANADIG_REG_CORE_REG2 (18)
|
||||
#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG 0x1000
|
||||
#define BM_ANADIG_ANA_MISC0_V2_STOP_MODE_CONFIG 0x800
|
||||
#define BM_ANADIG_ANA_MISC0_V3_STOP_MODE_CONFIG 0xc00
|
||||
#define BM_ANADIG_ANA_MISC2_REG1_STEP_TIME (0x3 << 26)
|
||||
#define BP_ANADIG_ANA_MISC2_REG1_STEP_TIME (26)
|
||||
/* Below MISC0_DISCON_HIGH_SNVS is only for i.MX6SL */
|
||||
#define BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS 0x2000
|
||||
/* Since i.MX6SX, DISCON_HIGH_SNVS is changed to bit 12 */
|
||||
#define BM_ANADIG_ANA_MISC0_V2_DISCON_HIGH_SNVS 0x1000
|
||||
#define BM_ANADIG_USB_CHRG_DETECT_CHK_CHRG_B 0x80000
|
||||
#define BM_ANADIG_USB_CHRG_DETECT_EN_B 0x100000
|
||||
|
||||
#define LDO_RAMP_UP_UNIT_IN_CYCLES 64 /* 64 cycles per step */
|
||||
#define LDO_RAMP_UP_FREQ_IN_MHZ 24 /* cycle based on 24M OSC */
|
||||
|
||||
static struct regmap *anatop;
|
||||
|
||||
static void imx_anatop_enable_weak2p5(bool enable)
|
||||
{
|
||||
u32 reg, val;
|
||||
u32 reg, val, mask;
|
||||
|
||||
regmap_read(anatop, ANADIG_ANA_MISC0, &val);
|
||||
|
||||
if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz() || cpu_is_imx6sll())
|
||||
mask = BM_ANADIG_ANA_MISC0_V3_STOP_MODE_CONFIG;
|
||||
else if (cpu_is_imx6sl())
|
||||
mask = BM_ANADIG_ANA_MISC0_V2_STOP_MODE_CONFIG;
|
||||
else
|
||||
mask = BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG;
|
||||
|
||||
/* can only be enabled when stop_mode_config is clear. */
|
||||
reg = ANADIG_REG_2P5;
|
||||
reg += (enable && (val & BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG) == 0) ?
|
||||
REG_SET : REG_CLR;
|
||||
reg += (enable && (val & mask) == 0) ? REG_SET : REG_CLR;
|
||||
regmap_write(anatop, reg, BM_ANADIG_REG_2P5_ENABLE_WEAK_LINREG);
|
||||
}
|
||||
|
||||
|
@ -65,35 +91,89 @@ static inline void imx_anatop_enable_2p5_pulldown(bool enable)
|
|||
|
||||
static inline void imx_anatop_disconnect_high_snvs(bool enable)
|
||||
{
|
||||
regmap_write(anatop, ANADIG_ANA_MISC0 + (enable ? REG_SET : REG_CLR),
|
||||
BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS);
|
||||
if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz() || cpu_is_imx6sll())
|
||||
regmap_write(anatop, ANADIG_ANA_MISC0 +
|
||||
(enable ? REG_SET : REG_CLR),
|
||||
BM_ANADIG_ANA_MISC0_V2_DISCON_HIGH_SNVS);
|
||||
else
|
||||
regmap_write(anatop, ANADIG_ANA_MISC0 +
|
||||
(enable ? REG_SET : REG_CLR),
|
||||
BM_ANADIG_ANA_MISC0_DISCON_HIGH_SNVS);
|
||||
}
|
||||
|
||||
static void imx_anatop_disable_pu(bool off)
|
||||
{
|
||||
u32 val, soc, delay;
|
||||
if (off) {
|
||||
regmap_read(anatop, ANADIG_REG_CORE, &val);
|
||||
val &= ~BM_ANADIG_REG_CORE_REG1;
|
||||
regmap_write(anatop, ANADIG_REG_CORE, val);
|
||||
} else {
|
||||
/* track vddpu with vddsoc */
|
||||
regmap_read(anatop, ANADIG_REG_CORE, &val);
|
||||
soc = val & BM_ANADIG_REG_CORE_REG2;
|
||||
val &= ~BM_ANADIG_REG_CORE_REG1;
|
||||
val |= soc >> 9;
|
||||
regmap_write(anatop, ANADIG_REG_CORE, val);
|
||||
/* wait PU LDO ramp */
|
||||
regmap_read(anatop, ANADIG_ANA_MISC2, &val);
|
||||
val &= BM_ANADIG_ANA_MISC2_REG1_STEP_TIME;
|
||||
val >>= BP_ANADIG_ANA_MISC2_REG1_STEP_TIME;
|
||||
delay = (soc >> BP_ANADIG_REG_CORE_REG2) *
|
||||
(LDO_RAMP_UP_UNIT_IN_CYCLES << val) /
|
||||
LDO_RAMP_UP_FREQ_IN_MHZ + 1;
|
||||
udelay(delay);
|
||||
}
|
||||
}
|
||||
|
||||
void imx_anatop_pre_suspend(void)
|
||||
{
|
||||
if (imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2)
|
||||
imx_anatop_enable_2p5_pulldown(true);
|
||||
else
|
||||
imx_anatop_enable_weak2p5(true);
|
||||
if (cpu_is_imx7d()) {
|
||||
/* PLL and PFDs overwrite set */
|
||||
regmap_write(anatop, ANADIG_ARM_PLL + REG_SET, 1 << 20);
|
||||
regmap_write(anatop, ANADIG_DDR_PLL + REG_SET, 1 << 19);
|
||||
regmap_write(anatop, ANADIG_SYS_PLL + REG_SET, 0x1ff << 17);
|
||||
regmap_write(anatop, ANADIG_ENET_PLL + REG_SET, 1 << 13);
|
||||
regmap_write(anatop, ANADIG_AUDIO_PLL + REG_SET, 1 << 24);
|
||||
regmap_write(anatop, ANADIG_VIDEO_PLL + REG_SET, 1 << 24);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0)
|
||||
imx_anatop_disable_pu(true);
|
||||
imx_anatop_enable_weak2p5(true);
|
||||
|
||||
imx_anatop_enable_fet_odrive(true);
|
||||
|
||||
if (cpu_is_imx6sl())
|
||||
if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul() ||
|
||||
cpu_is_imx6ull() || cpu_is_imx6ulz() || cpu_is_imx6sll())
|
||||
imx_anatop_disconnect_high_snvs(true);
|
||||
}
|
||||
|
||||
void imx_anatop_post_resume(void)
|
||||
{
|
||||
if (imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2)
|
||||
imx_anatop_enable_2p5_pulldown(false);
|
||||
else
|
||||
imx_anatop_enable_weak2p5(false);
|
||||
if (cpu_is_imx7d()) {
|
||||
/* PLL and PFDs overwrite clear */
|
||||
regmap_write(anatop, ANADIG_ARM_PLL + REG_CLR, 1 << 20);
|
||||
regmap_write(anatop, ANADIG_DDR_PLL + REG_CLR, 1 << 19);
|
||||
regmap_write(anatop, ANADIG_SYS_PLL + REG_CLR, 0x1ff << 17);
|
||||
regmap_write(anatop, ANADIG_ENET_PLL + REG_CLR, 1 << 13);
|
||||
regmap_write(anatop, ANADIG_AUDIO_PLL + REG_CLR, 1 << 24);
|
||||
regmap_write(anatop, ANADIG_VIDEO_PLL + REG_CLR, 1 << 24);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0)
|
||||
imx_anatop_disable_pu(false);
|
||||
|
||||
imx_anatop_enable_weak2p5(false);
|
||||
|
||||
imx_anatop_enable_fet_odrive(false);
|
||||
|
||||
if (cpu_is_imx6sl())
|
||||
if (cpu_is_imx6sl() || cpu_is_imx6sx() || cpu_is_imx6ul() ||
|
||||
cpu_is_imx6ull() || cpu_is_imx6ulz() || cpu_is_imx6sll())
|
||||
imx_anatop_disconnect_high_snvs(false);
|
||||
|
||||
}
|
||||
|
||||
static void imx_anatop_usb_chrg_detect_disable(void)
|
||||
|
@ -110,10 +190,11 @@ void __init imx_init_revision_from_anatop(void)
|
|||
{
|
||||
struct device_node *np;
|
||||
void __iomem *anatop_base;
|
||||
void __iomem *src_base;
|
||||
unsigned int revision;
|
||||
u32 digprog;
|
||||
u32 digprog, sbmr2 = 0;
|
||||
u16 offset = ANADIG_DIGPROG;
|
||||
u8 major_part, minor_part;
|
||||
u16 major_part, minor_part;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
|
||||
anatop_base = of_iomap(np, 0);
|
||||
|
@ -125,6 +206,20 @@ void __init imx_init_revision_from_anatop(void)
|
|||
digprog = readl_relaxed(anatop_base + offset);
|
||||
iounmap(anatop_base);
|
||||
|
||||
if ((digprog >> 16) == MXC_CPU_IMX6ULL) {
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-src");
|
||||
if (np) {
|
||||
src_base = of_iomap(np, 0);
|
||||
WARN_ON(!src_base);
|
||||
sbmr2 = readl_relaxed(src_base + 0x1c);
|
||||
iounmap(src_base);
|
||||
}
|
||||
if (sbmr2 & (1 << 6)) {
|
||||
digprog &= ~(0xff << 16);
|
||||
digprog |= (MXC_CPU_IMX6ULZ << 16);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* On i.MX7D digprog value match linux version format, so
|
||||
* it needn't map again and we can use register value directly.
|
||||
|
@ -144,24 +239,6 @@ void __init imx_init_revision_from_anatop(void)
|
|||
major_part = (digprog >> 8) & 0xf;
|
||||
minor_part = digprog & 0xf;
|
||||
revision = ((major_part + 1) << 4) | minor_part;
|
||||
|
||||
if ((digprog >> 16) == MXC_CPU_IMX6ULL) {
|
||||
void __iomem *src_base;
|
||||
u32 sbmr2;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL,
|
||||
"fsl,imx6ul-src");
|
||||
src_base = of_iomap(np, 0);
|
||||
WARN_ON(!src_base);
|
||||
sbmr2 = readl_relaxed(src_base + SRC_SBMR2);
|
||||
iounmap(src_base);
|
||||
|
||||
/* src_sbmr2 bit 6 is to identify if it is i.MX6ULZ */
|
||||
if (sbmr2 & (1 << 6)) {
|
||||
digprog &= ~(0xff << 16);
|
||||
digprog |= (MXC_CPU_IMX6ULZ << 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mxc_set_cpu_type(digprog >> 16 & 0xff);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,772 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2016 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file busfreq_ddr3.c
|
||||
*
|
||||
* @brief iMX6 DDR3 frequency change specific file.
|
||||
*
|
||||
* @ingroup PM
|
||||
*/
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <linux/busfreq-imx.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include "hardware.h"
|
||||
#include "common.h"
|
||||
|
||||
#define SMP_WFE_CODE_SIZE 0x400
|
||||
|
||||
#define MIN_DLL_ON_FREQ 333000000
|
||||
#define MAX_DLL_OFF_FREQ 125000000
|
||||
#define MMDC0_MPMUR0 0x8b8
|
||||
#define MMDC0_MPMUR0_OFFSET 16
|
||||
#define MMDC0_MPMUR0_MASK 0x3ff
|
||||
|
||||
/*
|
||||
* This structure is for passing necessary data for low level ocram
|
||||
* busfreq code(arch/arm/mach-imx/ddr3_freq_imx6.S), if this struct
|
||||
* definition is changed, the offset definition in
|
||||
* arch/arm/mach-imx/ddr3_freq_imx6.S must be also changed accordingly,
|
||||
* otherwise, the busfreq change function will be broken!
|
||||
*
|
||||
* This structure will be placed in front of the asm code on ocram.
|
||||
*/
|
||||
struct imx6_busfreq_info {
|
||||
u32 freq;
|
||||
void *ddr_settings;
|
||||
u32 dll_off;
|
||||
void *iomux_offsets;
|
||||
u32 mu_delay_val;
|
||||
} __aligned(8);
|
||||
|
||||
static struct imx6_busfreq_info *imx6_busfreq_info;
|
||||
|
||||
/* DDR settings */
|
||||
static unsigned long (*iram_ddr_settings)[2];
|
||||
static unsigned long (*normal_mmdc_settings)[2];
|
||||
static unsigned long (*iram_iomux_settings)[2];
|
||||
|
||||
static void __iomem *mmdc_base;
|
||||
static void __iomem *iomux_base;
|
||||
static void __iomem *gic_dist_base;
|
||||
|
||||
static int ddr_settings_size;
|
||||
static int iomux_settings_size;
|
||||
static int curr_ddr_rate;
|
||||
|
||||
void (*imx6_up_change_ddr_freq)(struct imx6_busfreq_info *busfreq_info);
|
||||
extern void imx6_up_ddr3_freq_change(struct imx6_busfreq_info *busfreq_info);
|
||||
void (*imx7d_change_ddr_freq)(u32 freq) = NULL;
|
||||
extern void imx7d_ddr3_freq_change(u32 freq);
|
||||
extern void imx_lpddr3_freq_change(u32 freq);
|
||||
|
||||
void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings,
|
||||
bool dll_mode, void *iomux_offsets) = NULL;
|
||||
|
||||
extern unsigned int ddr_normal_rate;
|
||||
extern int low_bus_freq_mode;
|
||||
extern int audio_bus_freq_mode;
|
||||
extern void mx6_ddr3_freq_change(u32 freq, void *ddr_settings,
|
||||
bool dll_mode, void *iomux_offsets);
|
||||
|
||||
extern unsigned long save_ttbr1(void);
|
||||
extern void restore_ttbr1(unsigned long ttbr1);
|
||||
extern unsigned long ddr_freq_change_iram_base;
|
||||
|
||||
extern unsigned long ddr_freq_change_total_size;
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
|
||||
extern unsigned long mx6_ddr3_freq_change_start asm("mx6_ddr3_freq_change_start");
|
||||
extern unsigned long mx6_ddr3_freq_change_end asm("mx6_ddr3_freq_change_end");
|
||||
extern unsigned long imx6_up_ddr3_freq_change_start asm("imx6_up_ddr3_freq_change_start");
|
||||
extern unsigned long imx6_up_ddr3_freq_change_end asm("imx6_up_ddr3_freq_change_end");
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static unsigned long wfe_freq_change_iram_base;
|
||||
volatile u32 *wait_for_ddr_freq_update;
|
||||
static unsigned int online_cpus;
|
||||
static u32 *irqs_used;
|
||||
|
||||
void (*wfe_change_ddr_freq)(u32 cpuid, u32 *ddr_freq_change_done);
|
||||
void (*imx7_wfe_change_ddr_freq)(u32 cpuid, u32 ocram_base);
|
||||
extern void wfe_smp_freq_change(u32 cpuid, u32 *ddr_freq_change_done);
|
||||
extern void imx7_smp_wfe(u32 cpuid, u32 ocram_base);
|
||||
extern unsigned long wfe_smp_freq_change_start asm("wfe_smp_freq_change_start");
|
||||
extern unsigned long wfe_smp_freq_change_end asm("wfe_smp_freq_change_end");
|
||||
extern void __iomem *scu_base;
|
||||
#endif
|
||||
|
||||
unsigned long ddr3_dll_mx6sx[][2] = {
|
||||
{0x0c, 0x0},
|
||||
{0x10, 0x0},
|
||||
{0x1C, 0x04008032},
|
||||
{0x1C, 0x00048031},
|
||||
{0x1C, 0x05208030},
|
||||
{0x1C, 0x04008040},
|
||||
{0x818, 0x0},
|
||||
{0x18, 0x0},
|
||||
};
|
||||
|
||||
unsigned long ddr3_calibration_mx6sx[][2] = {
|
||||
{0x83c, 0x0},
|
||||
{0x840, 0x0},
|
||||
{0x848, 0x0},
|
||||
{0x850, 0x0},
|
||||
};
|
||||
|
||||
unsigned long iomux_offsets_mx6sx[][2] = {
|
||||
{0x330, 0x0},
|
||||
{0x334, 0x0},
|
||||
{0x338, 0x0},
|
||||
{0x33c, 0x0},
|
||||
};
|
||||
|
||||
unsigned long iomux_offsets_mx6ul[][2] = {
|
||||
{0x280, 0x0},
|
||||
{0x284, 0x0},
|
||||
};
|
||||
|
||||
unsigned long ddr3_dll_mx6q[][2] = {
|
||||
{0x0c, 0x0},
|
||||
{0x10, 0x0},
|
||||
{0x1C, 0x04088032},
|
||||
{0x1C, 0x0408803a},
|
||||
{0x1C, 0x08408030},
|
||||
{0x1C, 0x08408038},
|
||||
{0x818, 0x0},
|
||||
{0x18, 0x0},
|
||||
};
|
||||
|
||||
unsigned long ddr3_calibration[][2] = {
|
||||
{0x83c, 0x0},
|
||||
{0x840, 0x0},
|
||||
{0x483c, 0x0},
|
||||
{0x4840, 0x0},
|
||||
{0x848, 0x0},
|
||||
{0x4848, 0x0},
|
||||
{0x850, 0x0},
|
||||
{0x4850, 0x0},
|
||||
};
|
||||
|
||||
unsigned long iomux_offsets_mx6q[][2] = {
|
||||
{0x5A8, 0x0},
|
||||
{0x5B0, 0x0},
|
||||
{0x524, 0x0},
|
||||
{0x51C, 0x0},
|
||||
{0x518, 0x0},
|
||||
{0x50C, 0x0},
|
||||
{0x5B8, 0x0},
|
||||
{0x5C0, 0x0},
|
||||
};
|
||||
|
||||
unsigned long ddr3_dll_mx6dl[][2] = {
|
||||
{0x0c, 0x0},
|
||||
{0x10, 0x0},
|
||||
{0x1C, 0x04008032},
|
||||
{0x1C, 0x0400803a},
|
||||
{0x1C, 0x07208030},
|
||||
{0x1C, 0x07208038},
|
||||
{0x818, 0x0},
|
||||
{0x18, 0x0},
|
||||
};
|
||||
|
||||
unsigned long iomux_offsets_mx6dl[][2] = {
|
||||
{0x4BC, 0x0},
|
||||
{0x4C0, 0x0},
|
||||
{0x4C4, 0x0},
|
||||
{0x4C8, 0x0},
|
||||
{0x4CC, 0x0},
|
||||
{0x4D0, 0x0},
|
||||
{0x4D4, 0x0},
|
||||
{0x4D8, 0x0},
|
||||
};
|
||||
|
||||
int can_change_ddr_freq(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* each active core apart from the one changing
|
||||
* the DDR frequency will execute this function.
|
||||
* the rest of the cores have to remain in WFE
|
||||
* state until the frequency is changed.
|
||||
*/
|
||||
static irqreturn_t wait_in_wfe_irq(int irq, void *dev_id)
|
||||
{
|
||||
u32 me;
|
||||
|
||||
me = smp_processor_id();
|
||||
#ifdef CONFIG_LOCAL_TIMERS
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
|
||||
&me);
|
||||
#endif
|
||||
if (cpu_is_imx7d())
|
||||
imx7_wfe_change_ddr_freq(0x8 * me,
|
||||
(u32)ddr_freq_change_iram_base);
|
||||
else
|
||||
wfe_change_ddr_freq(0xff << (me * 8),
|
||||
(u32 *)&iram_iomux_settings[0][1]);
|
||||
#ifdef CONFIG_LOCAL_TIMERS
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
|
||||
&me);
|
||||
#endif
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* change the DDR frequency. */
|
||||
int update_ddr_freq_imx_smp(int ddr_rate)
|
||||
{
|
||||
int me = 0;
|
||||
unsigned long ttbr1;
|
||||
bool dll_off = false;
|
||||
int i;
|
||||
#ifdef CONFIG_SMP
|
||||
unsigned int reg = 0;
|
||||
int cpu = 0;
|
||||
#endif
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
if (!can_change_ddr_freq())
|
||||
return -1;
|
||||
|
||||
if (ddr_rate == curr_ddr_rate)
|
||||
return 0;
|
||||
|
||||
printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate);
|
||||
|
||||
if (cpu_is_imx6()) {
|
||||
if ((mode == BUS_FREQ_LOW) || (mode == BUS_FREQ_AUDIO))
|
||||
dll_off = true;
|
||||
|
||||
iram_ddr_settings[0][0] = ddr_settings_size;
|
||||
iram_iomux_settings[0][0] = iomux_settings_size;
|
||||
if (ddr_rate == ddr_normal_rate) {
|
||||
for (i = 0; i < iram_ddr_settings[0][0]; i++) {
|
||||
iram_ddr_settings[i + 1][0] =
|
||||
normal_mmdc_settings[i][0];
|
||||
iram_ddr_settings[i + 1][1] =
|
||||
normal_mmdc_settings[i][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ensure that all Cores are in WFE. */
|
||||
local_irq_disable();
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
me = smp_processor_id();
|
||||
|
||||
/* Make sure all the online cores are active */
|
||||
while (1) {
|
||||
bool not_exited_busfreq = false;
|
||||
u32 reg = 0;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
if (cpu_is_imx7d())
|
||||
reg = *(wait_for_ddr_freq_update + 1);
|
||||
else if (cpu_is_imx6())
|
||||
reg = __raw_readl(scu_base + 0x08);
|
||||
|
||||
if (reg & (0x02 << (cpu * 8)))
|
||||
not_exited_busfreq = true;
|
||||
}
|
||||
if (!not_exited_busfreq)
|
||||
break;
|
||||
}
|
||||
|
||||
wmb();
|
||||
*wait_for_ddr_freq_update = 1;
|
||||
dsb();
|
||||
if (cpu_is_imx7d())
|
||||
online_cpus = *(wait_for_ddr_freq_update + 1);
|
||||
else if (cpu_is_imx6())
|
||||
online_cpus = readl_relaxed(scu_base + 0x08);
|
||||
for_each_online_cpu(cpu) {
|
||||
*((char *)(&online_cpus) + (u8)cpu) = 0x02;
|
||||
if (cpu != me) {
|
||||
/* set the interrupt to be pending in the GIC. */
|
||||
reg = 1 << (irqs_used[cpu] % 32);
|
||||
writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET
|
||||
+ (irqs_used[cpu] / 32) * 4);
|
||||
}
|
||||
}
|
||||
/* Wait for the other active CPUs to idle */
|
||||
while (1) {
|
||||
u32 reg = 0;
|
||||
|
||||
if (cpu_is_imx7d())
|
||||
reg = *(wait_for_ddr_freq_update + 1);
|
||||
else if (cpu_is_imx6())
|
||||
reg = readl_relaxed(scu_base + 0x08);
|
||||
reg |= (0x02 << (me * 8));
|
||||
if (reg == online_cpus)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Ensure iram_tlb_phys_addr is flushed to DDR. */
|
||||
__cpuc_flush_dcache_area(&iram_tlb_phys_addr,
|
||||
sizeof(iram_tlb_phys_addr));
|
||||
if (cpu_is_imx6())
|
||||
outer_clean_range(__pa(&iram_tlb_phys_addr),
|
||||
__pa(&iram_tlb_phys_addr + 1));
|
||||
|
||||
ttbr1 = save_ttbr1();
|
||||
/* Now we can change the DDR frequency. */
|
||||
if (cpu_is_imx7d())
|
||||
imx7d_change_ddr_freq(ddr_rate);
|
||||
else if (cpu_is_imx6())
|
||||
mx6_change_ddr_freq(ddr_rate, iram_ddr_settings,
|
||||
dll_off, iram_iomux_settings);
|
||||
restore_ttbr1(ttbr1);
|
||||
curr_ddr_rate = ddr_rate;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
wmb();
|
||||
/* DDR frequency change is done . */
|
||||
*wait_for_ddr_freq_update = 0;
|
||||
dsb();
|
||||
|
||||
/* wake up all the cores. */
|
||||
sev();
|
||||
#endif
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
printk(KERN_DEBUG "Bus freq set to %d done! cpu=%d\n", ddr_rate, me);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Used by i.MX6SX/i.MX6UL for updating the ddr frequency */
|
||||
int update_ddr_freq_imx6_up(int ddr_rate)
|
||||
{
|
||||
int i;
|
||||
bool dll_off = false;
|
||||
unsigned long ttbr1;
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
if (ddr_rate == curr_ddr_rate)
|
||||
return 0;
|
||||
|
||||
printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate);
|
||||
|
||||
if ((mode == BUS_FREQ_LOW) || (mode == BUS_FREQ_AUDIO))
|
||||
dll_off = true;
|
||||
|
||||
imx6_busfreq_info->dll_off = dll_off;
|
||||
iram_ddr_settings[0][0] = ddr_settings_size;
|
||||
iram_iomux_settings[0][0] = iomux_settings_size;
|
||||
for (i = 0; i < iram_ddr_settings[0][0]; i++) {
|
||||
iram_ddr_settings[i + 1][0] =
|
||||
normal_mmdc_settings[i][0];
|
||||
iram_ddr_settings[i + 1][1] =
|
||||
normal_mmdc_settings[i][1];
|
||||
}
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
ttbr1 = save_ttbr1();
|
||||
imx6_busfreq_info->freq = ddr_rate;
|
||||
imx6_busfreq_info->ddr_settings = iram_ddr_settings;
|
||||
imx6_busfreq_info->iomux_offsets = iram_iomux_settings;
|
||||
imx6_busfreq_info->mu_delay_val = ((readl_relaxed(mmdc_base + MMDC0_MPMUR0)
|
||||
>> MMDC0_MPMUR0_OFFSET) & MMDC0_MPMUR0_MASK);
|
||||
|
||||
imx6_up_change_ddr_freq(imx6_busfreq_info);
|
||||
restore_ttbr1(ttbr1);
|
||||
curr_ddr_rate = ddr_rate;
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
printk(KERN_DEBUG "Bus freq set to %d done!\n", ddr_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_ddrc_ddr_settings(struct platform_device *busfreq_pdev)
|
||||
{
|
||||
int ddr_type = imx_ddrc_get_ddr_type();
|
||||
#ifdef CONFIG_SMP
|
||||
struct device_node *node;
|
||||
u32 cpu;
|
||||
struct device *dev = &busfreq_pdev->dev;
|
||||
int err;
|
||||
struct irq_data *d;
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "arm,cortex-a7-gic");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find imx7d-a7-gic device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
gic_dist_base = of_iomap(node, 0);
|
||||
WARN(!gic_dist_base, "unable to map gic dist registers\n");
|
||||
|
||||
irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(),
|
||||
GFP_KERNEL);
|
||||
for_each_online_cpu(cpu) {
|
||||
int irq;
|
||||
/*
|
||||
* set up a reserved interrupt to get all
|
||||
* the active cores into a WFE state
|
||||
* before changing the DDR frequency.
|
||||
*/
|
||||
irq = platform_get_irq(busfreq_pdev, cpu);
|
||||
err = request_irq(irq, wait_in_wfe_irq,
|
||||
IRQF_PERCPU, "ddrc", NULL);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Busfreq:request_irq failed %d, err = %d\n",
|
||||
irq, err);
|
||||
return err;
|
||||
}
|
||||
err = irq_set_affinity(irq, cpumask_of(cpu));
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Busfreq: Cannot set irq affinity irq=%d\n",
|
||||
irq);
|
||||
return err;
|
||||
}
|
||||
d = irq_get_irq_data(irq);
|
||||
irqs_used[cpu] = d->hwirq + 32;
|
||||
}
|
||||
|
||||
/* Store the variable used to communicate between cores */
|
||||
wait_for_ddr_freq_update = (u32 *)ddr_freq_change_iram_base;
|
||||
imx7_wfe_change_ddr_freq = (void *)fncpy(
|
||||
(void *)ddr_freq_change_iram_base + 0x8,
|
||||
&imx7_smp_wfe, SMP_WFE_CODE_SIZE - 0x8);
|
||||
#endif
|
||||
if (ddr_type == IMX_DDR_TYPE_DDR3)
|
||||
imx7d_change_ddr_freq = (void *)fncpy(
|
||||
(void *)ddr_freq_change_iram_base + SMP_WFE_CODE_SIZE,
|
||||
&imx7d_ddr3_freq_change,
|
||||
MX7_BUSFREQ_OCRAM_SIZE - SMP_WFE_CODE_SIZE);
|
||||
else if (ddr_type == IMX_DDR_TYPE_LPDDR3
|
||||
|| ddr_type == IMX_DDR_TYPE_LPDDR2)
|
||||
imx7d_change_ddr_freq = (void *)fncpy(
|
||||
(void *)ddr_freq_change_iram_base +
|
||||
SMP_WFE_CODE_SIZE,
|
||||
&imx_lpddr3_freq_change,
|
||||
MX7_BUSFREQ_OCRAM_SIZE - SMP_WFE_CODE_SIZE);
|
||||
|
||||
curr_ddr_rate = ddr_normal_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Used by i.MX6SX/i.MX6UL for mmdc setting init. */
|
||||
int init_mmdc_ddr3_settings_imx6_up(struct platform_device *busfreq_pdev)
|
||||
{
|
||||
int i;
|
||||
struct device_node *node;
|
||||
unsigned long ddr_code_size;
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find mmdc device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mmdc_base = of_iomap(node, 0);
|
||||
WARN(!mmdc_base, "unable to map mmdc registers\n");
|
||||
|
||||
if (cpu_is_imx6sx())
|
||||
node = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-iomuxc");
|
||||
else
|
||||
node = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-iomuxc");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find iomuxc device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
iomux_base = of_iomap(node, 0);
|
||||
WARN(!iomux_base, "unable to map iomux registers\n");
|
||||
|
||||
ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6sx) +
|
||||
ARRAY_SIZE(ddr3_calibration_mx6sx);
|
||||
|
||||
normal_mmdc_settings = kmalloc((ddr_settings_size * 8), GFP_KERNEL);
|
||||
memcpy(normal_mmdc_settings, ddr3_dll_mx6sx,
|
||||
sizeof(ddr3_dll_mx6sx));
|
||||
memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6sx)),
|
||||
ddr3_calibration_mx6sx, sizeof(ddr3_calibration_mx6sx));
|
||||
|
||||
/* store the original DDR settings at boot. */
|
||||
for (i = 0; i < ddr_settings_size; i++) {
|
||||
/*
|
||||
* writes via command mode register cannot be read back.
|
||||
* hence hardcode them in the initial static array.
|
||||
* this may require modification on a per customer basis.
|
||||
*/
|
||||
if (normal_mmdc_settings[i][0] != 0x1C)
|
||||
normal_mmdc_settings[i][1] =
|
||||
readl_relaxed(mmdc_base
|
||||
+ normal_mmdc_settings[i][0]);
|
||||
}
|
||||
|
||||
if (cpu_is_imx6ul() || cpu_is_imx6ull() || cpu_is_imx6ulz())
|
||||
iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6ul);
|
||||
else
|
||||
iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6sx);
|
||||
|
||||
ddr_code_size = (&imx6_up_ddr3_freq_change_end -&imx6_up_ddr3_freq_change_start) *4 +
|
||||
sizeof(*imx6_busfreq_info);
|
||||
imx6_busfreq_info = (struct imx6_busfreq_info *)ddr_freq_change_iram_base;
|
||||
|
||||
imx6_up_change_ddr_freq = (void *)fncpy((void *)ddr_freq_change_iram_base + sizeof(*imx6_busfreq_info),
|
||||
&imx6_up_ddr3_freq_change, ddr_code_size - sizeof(*imx6_busfreq_info));
|
||||
|
||||
/*
|
||||
* Store the size of the array in iRAM also,
|
||||
* increase the size by 8 bytes.
|
||||
*/
|
||||
iram_iomux_settings = (void *)(ddr_freq_change_iram_base + ddr_code_size);
|
||||
iram_ddr_settings = iram_iomux_settings + (iomux_settings_size * 8) + 8;
|
||||
|
||||
if ((ddr_code_size + (iomux_settings_size + ddr_settings_size) * 8 + 16)
|
||||
> ddr_freq_change_total_size) {
|
||||
printk(KERN_ERR "Not enough memory allocated for DDR Frequency change code.\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < iomux_settings_size; i++) {
|
||||
if (cpu_is_imx6ul() || cpu_is_imx6ull() || cpu_is_imx6ulz()) {
|
||||
iomux_offsets_mx6ul[i][1] =
|
||||
readl_relaxed(iomux_base +
|
||||
iomux_offsets_mx6ul[i][0]);
|
||||
iram_iomux_settings[i + 1][0] =
|
||||
iomux_offsets_mx6ul[i][0];
|
||||
iram_iomux_settings[i + 1][1] =
|
||||
iomux_offsets_mx6ul[i][1];
|
||||
} else {
|
||||
iomux_offsets_mx6sx[i][1] =
|
||||
readl_relaxed(iomux_base +
|
||||
iomux_offsets_mx6sx[i][0]);
|
||||
iram_iomux_settings[i + 1][0] =
|
||||
iomux_offsets_mx6sx[i][0];
|
||||
iram_iomux_settings[i + 1][1] =
|
||||
iomux_offsets_mx6sx[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
curr_ddr_rate = ddr_normal_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_mmdc_ddr3_settings_imx6_smp(struct platform_device *busfreq_pdev)
|
||||
{
|
||||
int i;
|
||||
struct device_node *node;
|
||||
unsigned long ddr_code_size;
|
||||
unsigned long wfe_code_size = 0;
|
||||
#ifdef CONFIG_SMP
|
||||
u32 cpu;
|
||||
struct device *dev = &busfreq_pdev->dev;
|
||||
int err;
|
||||
struct irq_data *d;
|
||||
#endif
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc-combine");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find imx6q-mmdc device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
mmdc_base = of_iomap(node, 0);
|
||||
WARN(!mmdc_base, "unable to map mmdc registers\n");
|
||||
|
||||
node = NULL;
|
||||
if (cpu_is_imx6q())
|
||||
node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-iomuxc");
|
||||
if (cpu_is_imx6dl())
|
||||
node = of_find_compatible_node(NULL, NULL,
|
||||
"fsl,imx6dl-iomuxc");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find imx6q-iomux device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
iomux_base = of_iomap(node, 0);
|
||||
WARN(!iomux_base, "unable to map iomux registers\n");
|
||||
|
||||
node = NULL;
|
||||
node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find imx6q-a9-gic device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
gic_dist_base = of_iomap(node, 0);
|
||||
WARN(!gic_dist_base, "unable to map gic dist registers\n");
|
||||
|
||||
if (cpu_is_imx6q())
|
||||
ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6q) +
|
||||
ARRAY_SIZE(ddr3_calibration);
|
||||
if (cpu_is_imx6dl())
|
||||
ddr_settings_size = ARRAY_SIZE(ddr3_dll_mx6dl) +
|
||||
ARRAY_SIZE(ddr3_calibration);
|
||||
|
||||
normal_mmdc_settings = kmalloc((ddr_settings_size * 8), GFP_KERNEL);
|
||||
if (cpu_is_imx6q()) {
|
||||
memcpy(normal_mmdc_settings, ddr3_dll_mx6q,
|
||||
sizeof(ddr3_dll_mx6q));
|
||||
memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6q)),
|
||||
ddr3_calibration, sizeof(ddr3_calibration));
|
||||
}
|
||||
if (cpu_is_imx6dl()) {
|
||||
memcpy(normal_mmdc_settings, ddr3_dll_mx6dl,
|
||||
sizeof(ddr3_dll_mx6dl));
|
||||
memcpy(((char *)normal_mmdc_settings + sizeof(ddr3_dll_mx6dl)),
|
||||
ddr3_calibration, sizeof(ddr3_calibration));
|
||||
}
|
||||
/* store the original DDR settings at boot. */
|
||||
for (i = 0; i < ddr_settings_size; i++) {
|
||||
/*
|
||||
* writes via command mode register cannot be read back.
|
||||
* hence hardcode them in the initial static array.
|
||||
* this may require modification on a per customer basis.
|
||||
*/
|
||||
if (normal_mmdc_settings[i][0] != 0x1C)
|
||||
normal_mmdc_settings[i][1] =
|
||||
readl_relaxed(mmdc_base
|
||||
+ normal_mmdc_settings[i][0]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(),
|
||||
GFP_KERNEL);
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
int irq;
|
||||
|
||||
/*
|
||||
* set up a reserved interrupt to get all
|
||||
* the active cores into a WFE state
|
||||
* before changing the DDR frequency.
|
||||
*/
|
||||
irq = platform_get_irq(busfreq_pdev, cpu);
|
||||
err = request_irq(irq, wait_in_wfe_irq,
|
||||
IRQF_PERCPU, "mmdc_1", NULL);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Busfreq:request_irq failed %d, err = %d\n",
|
||||
irq, err);
|
||||
return err;
|
||||
}
|
||||
err = irq_set_affinity(irq, cpumask_of(cpu));
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Busfreq: Cannot set irq affinity irq=%d,\n",
|
||||
irq);
|
||||
return err;
|
||||
}
|
||||
d = irq_get_irq_data(irq);
|
||||
irqs_used[cpu] = d->hwirq + 32;
|
||||
}
|
||||
#endif
|
||||
iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q);
|
||||
|
||||
ddr_code_size = (&mx6_ddr3_freq_change_end -
|
||||
&mx6_ddr3_freq_change_start) * 4;
|
||||
|
||||
mx6_change_ddr_freq = (void *)fncpy((void *)ddr_freq_change_iram_base,
|
||||
&mx6_ddr3_freq_change, ddr_code_size);
|
||||
|
||||
/*
|
||||
* Store the size of the array in iRAM also,
|
||||
* increase the size by 8 bytes.
|
||||
*/
|
||||
iram_iomux_settings = (void *)(ddr_freq_change_iram_base +
|
||||
ddr_code_size);
|
||||
iram_ddr_settings = iram_iomux_settings + (iomux_settings_size * 8) + 8;
|
||||
#ifdef CONFIG_SMP
|
||||
wfe_freq_change_iram_base = (unsigned long)((u32 *)iram_ddr_settings +
|
||||
(ddr_settings_size * 8) + 8);
|
||||
|
||||
if (wfe_freq_change_iram_base & (FNCPY_ALIGN - 1))
|
||||
wfe_freq_change_iram_base += FNCPY_ALIGN -
|
||||
((uintptr_t)wfe_freq_change_iram_base % (FNCPY_ALIGN));
|
||||
|
||||
wfe_code_size = (&wfe_smp_freq_change_end -
|
||||
&wfe_smp_freq_change_start) *4;
|
||||
|
||||
wfe_change_ddr_freq = (void *)fncpy((void *)wfe_freq_change_iram_base,
|
||||
&wfe_smp_freq_change, wfe_code_size);
|
||||
|
||||
/*
|
||||
* Store the variable used to communicate
|
||||
* between cores in a non-cacheable IRAM area
|
||||
*/
|
||||
wait_for_ddr_freq_update = (u32 *)&iram_iomux_settings[0][1];
|
||||
#endif
|
||||
|
||||
if ((ddr_code_size + wfe_code_size + (iomux_settings_size +
|
||||
ddr_settings_size) * 8 + 16)
|
||||
> ddr_freq_change_total_size) {
|
||||
printk(KERN_ERR "Not enough memory for DDR Freq scale.\n");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (cpu_is_imx6q()) {
|
||||
/* store the IOMUX settings at boot. */
|
||||
for (i = 0; i < iomux_settings_size; i++) {
|
||||
iomux_offsets_mx6q[i][1] =
|
||||
readl_relaxed(iomux_base +
|
||||
iomux_offsets_mx6q[i][0]);
|
||||
iram_iomux_settings[i + 1][0] =
|
||||
iomux_offsets_mx6q[i][0];
|
||||
iram_iomux_settings[i + 1][1] =
|
||||
iomux_offsets_mx6q[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_is_imx6dl()) {
|
||||
for (i = 0; i < iomux_settings_size; i++) {
|
||||
iomux_offsets_mx6dl[i][1] =
|
||||
readl_relaxed(iomux_base +
|
||||
iomux_offsets_mx6dl[i][0]);
|
||||
iram_iomux_settings[i + 1][0] =
|
||||
iomux_offsets_mx6dl[i][0];
|
||||
iram_iomux_settings[i + 1][1] =
|
||||
iomux_offsets_mx6dl[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
curr_ddr_rate = ddr_normal_rate;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2016 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright 2017 NXP.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @file busfreq_lpddr2.c
|
||||
*
|
||||
* @brief iMX6 LPDDR2 frequency change specific file.
|
||||
*
|
||||
* @ingroup PM
|
||||
*/
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/tlb.h>
|
||||
#include <linux/busfreq-imx.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "hardware.h"
|
||||
|
||||
static struct device *busfreq_dev;
|
||||
static int curr_ddr_rate;
|
||||
static DEFINE_SPINLOCK(freq_lock);
|
||||
|
||||
void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode) = NULL;
|
||||
|
||||
extern unsigned int ddr_normal_rate;
|
||||
extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode);
|
||||
extern void imx6_up_lpddr2_freq_change(u32 freq, int bus_freq_mode);
|
||||
extern void imx6sll_lpddr2_freq_change(u32 freq, int bus_freq_mode);
|
||||
extern unsigned long save_ttbr1(void);
|
||||
extern void restore_ttbr1(unsigned long ttbr1);
|
||||
extern void mx6q_lpddr2_freq_change(u32 freq, void *ddr_settings);
|
||||
extern unsigned long ddr_freq_change_iram_base;
|
||||
extern unsigned long imx6_lpddr2_freq_change_start asm("imx6_lpddr2_freq_change_start");
|
||||
extern unsigned long imx6_lpddr2_freq_change_end asm("imx6_lpddr2_freq_change_end");
|
||||
extern unsigned long mx6q_lpddr2_freq_change_start asm("mx6q_lpddr2_freq_change_start");
|
||||
extern unsigned long mx6q_lpddr2_freq_change_end asm("mx6q_lpddr2_freq_change_end");
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
|
||||
struct mmdc_settings_info {
|
||||
u32 size;
|
||||
void *settings;
|
||||
int freq;
|
||||
} __aligned(8);
|
||||
static struct mmdc_settings_info *mmdc_settings_info;
|
||||
void (*mx6_change_lpddr2_freq_smp)(u32 ddr_freq, struct mmdc_settings_info
|
||||
*mmdc_settings_info) = NULL;
|
||||
|
||||
static int mmdc_settings_size;
|
||||
static unsigned long (*mmdc_settings)[2];
|
||||
static unsigned long (*iram_mmdc_settings)[2];
|
||||
static unsigned long *iram_settings_size;
|
||||
static unsigned long *iram_ddr_freq_chage;
|
||||
unsigned long mmdc_timing_settings[][2] = {
|
||||
{0x0C, 0x0}, /* mmdc_mdcfg0 */
|
||||
{0x10, 0x0}, /* mmdc_mdcfg1 */
|
||||
{0x14, 0x0}, /* mmdc_mdcfg2 */
|
||||
{0x18, 0x0}, /* mmdc_mdmisc */
|
||||
{0x38, 0x0}, /* mmdc_mdcfg3lp */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
volatile u32 *wait_for_lpddr2_freq_update;
|
||||
static unsigned int online_cpus;
|
||||
static u32 *irqs_used;
|
||||
void (*wfe_change_lpddr2_freq)(u32 cpuid, u32 *ddr_freq_change_done);
|
||||
extern void wfe_smp_freq_change(u32 cpuid, u32 *ddr_freq_change_done);
|
||||
extern unsigned long wfe_smp_freq_change_start asm("wfe_smp_freq_change_start");
|
||||
extern unsigned long wfe_smp_freq_change_end asm("wfe_smp_freq_change_end");
|
||||
extern void __iomem *scu_base;
|
||||
static void __iomem *gic_dist_base;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static irqreturn_t wait_in_wfe_irq(int irq, void *dev_id)
|
||||
{
|
||||
u32 me;
|
||||
|
||||
me = smp_processor_id();
|
||||
#ifdef CONFIG_LOCAL_TIMERS
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &me);
|
||||
#endif
|
||||
wfe_change_lpddr2_freq(0xff << (me * 8),
|
||||
(u32 *)ddr_freq_change_iram_base);
|
||||
#ifdef CONFIG_LOCAL_TIMERS
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &me);
|
||||
#endif
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* change the DDR frequency. */
|
||||
int update_lpddr2_freq(int ddr_rate)
|
||||
{
|
||||
unsigned long ttbr1, flags;
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
if (ddr_rate == curr_ddr_rate)
|
||||
return 0;
|
||||
|
||||
printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate);
|
||||
|
||||
spin_lock_irqsave(&freq_lock, flags);
|
||||
/*
|
||||
* Flush the TLB, to ensure no TLB maintenance occurs
|
||||
* when DDR is in self-refresh.
|
||||
*/
|
||||
ttbr1 = save_ttbr1();
|
||||
|
||||
/* Now change DDR frequency. */
|
||||
if (cpu_is_imx6sl())
|
||||
mx6_change_lpddr2_freq(ddr_rate,
|
||||
(mode == BUS_FREQ_LOW || mode == BUS_FREQ_ULTRA_LOW) ? 1 : 0);
|
||||
else
|
||||
mx6_change_lpddr2_freq(ddr_rate,
|
||||
(mode == BUS_FREQ_LOW || mode == BUS_FREQ_AUDIO) ? 1 : 0);
|
||||
|
||||
restore_ttbr1(ttbr1);
|
||||
|
||||
curr_ddr_rate = ddr_rate;
|
||||
spin_unlock_irqrestore(&freq_lock, flags);
|
||||
|
||||
printk(KERN_DEBUG "\nBus freq set to %d done...\n", ddr_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev)
|
||||
{
|
||||
unsigned long ddr_code_size;
|
||||
busfreq_dev = &busfreq_pdev->dev;
|
||||
|
||||
ddr_code_size = SZ_4K;
|
||||
|
||||
if (cpu_is_imx6sl())
|
||||
mx6_change_lpddr2_freq = (void *)fncpy(
|
||||
(void *)ddr_freq_change_iram_base,
|
||||
&mx6_lpddr2_freq_change, ddr_code_size);
|
||||
if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz())
|
||||
mx6_change_lpddr2_freq = (void *)fncpy(
|
||||
(void *)ddr_freq_change_iram_base,
|
||||
&imx6_up_lpddr2_freq_change, ddr_code_size);
|
||||
if (cpu_is_imx6sll())
|
||||
mx6_change_lpddr2_freq = (void *)fncpy(
|
||||
(void *)ddr_freq_change_iram_base,
|
||||
&imx6sll_lpddr2_freq_change, ddr_code_size);
|
||||
|
||||
curr_ddr_rate = ddr_normal_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_lpddr2_freq_smp(int ddr_rate)
|
||||
{
|
||||
unsigned long ttbr1;
|
||||
int i, me = 0;
|
||||
#ifdef CONFIG_SMP
|
||||
int cpu = 0;
|
||||
u32 reg = 0;
|
||||
#endif
|
||||
|
||||
if (ddr_rate == curr_ddr_rate)
|
||||
return 0;
|
||||
|
||||
printk(KERN_DEBUG "Bus freq set to %d start...\n", ddr_rate);
|
||||
|
||||
for (i=0; i < mmdc_settings_size; i++) {
|
||||
iram_mmdc_settings[i][0] = mmdc_settings[i][0];
|
||||
iram_mmdc_settings[i][1] = mmdc_settings[i][1];
|
||||
}
|
||||
|
||||
mmdc_settings_info->size = mmdc_settings_size;
|
||||
mmdc_settings_info->settings = iram_mmdc_settings;
|
||||
mmdc_settings_info->freq = curr_ddr_rate;
|
||||
|
||||
/* ensure that all Cores are in WFE. */
|
||||
local_irq_disable();
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
me = smp_processor_id();
|
||||
|
||||
/* Make sure all the online cores are active */
|
||||
while (1) {
|
||||
bool not_exited_busfreq = false;
|
||||
for_each_online_cpu(cpu) {
|
||||
reg = __raw_readl(scu_base + 0x08);
|
||||
if (reg & (0x02 << (cpu * 8)))
|
||||
not_exited_busfreq = true;
|
||||
}
|
||||
if (!not_exited_busfreq)
|
||||
break;
|
||||
}
|
||||
|
||||
wmb();
|
||||
*wait_for_lpddr2_freq_update = 1;
|
||||
dsb();
|
||||
online_cpus = readl_relaxed(scu_base + 0x08);
|
||||
for_each_online_cpu(cpu) {
|
||||
*((char *)(&online_cpus) + (u8)cpu) = 0x02;
|
||||
if (cpu != me) {
|
||||
reg = 1 << (irqs_used[cpu] % 32);
|
||||
writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET
|
||||
+ (irqs_used[cpu] / 32) * 4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the other active CPUs to idle */
|
||||
while (1) {
|
||||
reg = 0;
|
||||
reg = readl_relaxed(scu_base + 0x08);
|
||||
reg |= (0x02 << (me * 8));
|
||||
if (reg == online_cpus)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Ensure iram_tlb_phys_addr is flushed to DDR. */
|
||||
__cpuc_flush_dcache_area(&iram_tlb_phys_addr,
|
||||
sizeof(iram_tlb_phys_addr));
|
||||
outer_clean_range(__pa(&iram_tlb_phys_addr),
|
||||
__pa(&iram_tlb_phys_addr + 1));
|
||||
/*
|
||||
* Flush the TLB, to ensure no TLB maintenance occurs
|
||||
* when DDR is in self-refresh.
|
||||
*/
|
||||
ttbr1 = save_ttbr1();
|
||||
|
||||
curr_ddr_rate = ddr_rate;
|
||||
|
||||
/* Now change DDR frequency. */
|
||||
mx6_change_lpddr2_freq_smp(ddr_rate, mmdc_settings_info);
|
||||
|
||||
restore_ttbr1(ttbr1);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
wmb();
|
||||
/* DDR frequency change is done . */
|
||||
*wait_for_lpddr2_freq_update = 0;
|
||||
dsb();
|
||||
/* wake up all the cores. */
|
||||
sev();
|
||||
#endif
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
printk(KERN_DEBUG "Bus freq set to %d done! cpu=%d\n", ddr_rate, me);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_mmdc_lpddr2_settings_mx6q(struct platform_device *busfreq_pdev)
|
||||
{
|
||||
struct device *dev = &busfreq_pdev->dev;
|
||||
unsigned long ddr_code_size = 0;
|
||||
unsigned long wfe_code_size = 0;
|
||||
struct device_node *node;
|
||||
void __iomem *mmdc_base;
|
||||
int i;
|
||||
#ifdef CONFIG_SMP
|
||||
struct irq_data *d;
|
||||
u32 cpu;
|
||||
int err;
|
||||
#endif
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find mmdc device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mmdc_base = of_iomap(node, 0);
|
||||
if (!mmdc_base) {
|
||||
dev_err(dev, "unable to map mmdc registers\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mmdc_settings_size = ARRAY_SIZE(mmdc_timing_settings);
|
||||
mmdc_settings = kmalloc((mmdc_settings_size * 8), GFP_KERNEL);
|
||||
memcpy(mmdc_settings, mmdc_timing_settings,
|
||||
sizeof(mmdc_timing_settings));
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic");
|
||||
if (!node) {
|
||||
printk(KERN_ERR "failed to find imx6q-a9-gic device tree data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gic_dist_base = of_iomap(node, 0);
|
||||
WARN(!gic_dist_base, "unable to map gic dist registers\n");
|
||||
|
||||
irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(),
|
||||
GFP_KERNEL);
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
int irq = platform_get_irq(busfreq_pdev, cpu);
|
||||
err = request_irq(irq, wait_in_wfe_irq, IRQF_PERCPU,
|
||||
"mmdc_1", NULL);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Busfreq:request_irq failed %d, err = %d\n",
|
||||
irq, err);
|
||||
return err;
|
||||
}
|
||||
err = irq_set_affinity(irq, cpumask_of(cpu));
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
"Busfreq: Cannot set irq affinity irq=%d,\n",
|
||||
irq);
|
||||
return err;
|
||||
}
|
||||
d = irq_get_irq_data(irq);
|
||||
irqs_used[cpu] = d->hwirq + 32;
|
||||
}
|
||||
|
||||
/* Stoange_iram_basee the variable used to communicate between cores in
|
||||
* a non-cacheable IRAM area */
|
||||
wait_for_lpddr2_freq_update = (u32 *)ddr_freq_change_iram_base;
|
||||
wfe_code_size = (&wfe_smp_freq_change_end - &wfe_smp_freq_change_start) *4;
|
||||
|
||||
wfe_change_lpddr2_freq = (void *)fncpy((void *)ddr_freq_change_iram_base + 0x8,
|
||||
&wfe_smp_freq_change, wfe_code_size);
|
||||
#endif
|
||||
iram_settings_size = (void *)ddr_freq_change_iram_base + wfe_code_size + 0x8;
|
||||
iram_mmdc_settings = (void *)iram_settings_size + sizeof(*mmdc_settings_info);
|
||||
iram_ddr_freq_chage = (void *)iram_mmdc_settings + (mmdc_settings_size * 8) + 0x8;
|
||||
mmdc_settings_info = (struct mmdc_settings_info *)iram_settings_size;
|
||||
|
||||
ddr_code_size = (&mx6q_lpddr2_freq_change_end -&mx6q_lpddr2_freq_change_start) *4;
|
||||
|
||||
mx6_change_lpddr2_freq_smp = (void *)fncpy(iram_ddr_freq_chage,
|
||||
&mx6q_lpddr2_freq_change, ddr_code_size);
|
||||
|
||||
/* save initial mmdc boot timing settings */
|
||||
for (i=0; i < mmdc_settings_size; i++)
|
||||
mmdc_settings[i][1] = readl_relaxed(mmdc_base +
|
||||
mmdc_settings[i][0]);
|
||||
|
||||
curr_ddr_rate = ddr_normal_rate;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_net.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "hardware.h"
|
||||
|
||||
unsigned long iram_tlb_base_addr;
|
||||
unsigned long iram_tlb_phys_addr;
|
||||
|
||||
unsigned long save_ttbr1(void)
|
||||
{
|
||||
unsigned long lttbr1;
|
||||
asm volatile(
|
||||
".align 4\n"
|
||||
"mrc p15, 0, %0, c2, c0, 1\n"
|
||||
: "=r" (lttbr1)
|
||||
);
|
||||
return lttbr1;
|
||||
}
|
||||
|
||||
void restore_ttbr1(unsigned long ttbr1)
|
||||
{
|
||||
asm volatile(
|
||||
".align 4\n"
|
||||
"mcr p15, 0, %0, c2, c0, 1\n"
|
||||
: : "r" (ttbr1)
|
||||
);
|
||||
}
|
||||
|
||||
#define OCOTP_MAC_OFF (cpu_is_imx7d() ? 0x640 : 0x620)
|
||||
#define OCOTP_MACn(n) (OCOTP_MAC_OFF + (n) * 0x10)
|
||||
void __init imx6_enet_mac_init(const char *enet_compat, const char *ocotp_compat)
|
||||
{
|
||||
struct device_node *ocotp_np, *enet_np, *from = NULL;
|
||||
void __iomem *base;
|
||||
struct property *newmac;
|
||||
u32 macaddr_low;
|
||||
u32 macaddr_high = 0;
|
||||
u32 macaddr1_high = 0;
|
||||
u8 *macaddr;
|
||||
int i, id;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
enet_np = of_find_compatible_node(from, NULL, enet_compat);
|
||||
if (!enet_np)
|
||||
return;
|
||||
|
||||
from = enet_np;
|
||||
|
||||
if (of_get_mac_address(enet_np))
|
||||
goto put_enet_node;
|
||||
|
||||
id = of_alias_get_id(enet_np, "ethernet");
|
||||
if (id < 0)
|
||||
id = i;
|
||||
|
||||
ocotp_np = of_find_compatible_node(NULL, NULL, ocotp_compat);
|
||||
if (!ocotp_np) {
|
||||
pr_warn("failed to find ocotp node\n");
|
||||
goto put_enet_node;
|
||||
}
|
||||
|
||||
base = of_iomap(ocotp_np, 0);
|
||||
if (!base) {
|
||||
pr_warn("failed to map ocotp\n");
|
||||
goto put_ocotp_node;
|
||||
}
|
||||
|
||||
macaddr_low = readl_relaxed(base + OCOTP_MACn(1));
|
||||
if (id)
|
||||
macaddr1_high = readl_relaxed(base + OCOTP_MACn(2));
|
||||
else
|
||||
macaddr_high = readl_relaxed(base + OCOTP_MACn(0));
|
||||
|
||||
newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL);
|
||||
if (!newmac)
|
||||
goto put_ocotp_node;
|
||||
|
||||
newmac->value = newmac + 1;
|
||||
newmac->length = 6;
|
||||
newmac->name = kstrdup("local-mac-address", GFP_KERNEL);
|
||||
if (!newmac->name) {
|
||||
kfree(newmac);
|
||||
goto put_ocotp_node;
|
||||
}
|
||||
|
||||
macaddr = newmac->value;
|
||||
if (id) {
|
||||
macaddr[5] = (macaddr_low >> 16) & 0xff;
|
||||
macaddr[4] = (macaddr_low >> 24) & 0xff;
|
||||
macaddr[3] = macaddr1_high & 0xff;
|
||||
macaddr[2] = (macaddr1_high >> 8) & 0xff;
|
||||
macaddr[1] = (macaddr1_high >> 16) & 0xff;
|
||||
macaddr[0] = (macaddr1_high >> 24) & 0xff;
|
||||
} else {
|
||||
macaddr[5] = macaddr_high & 0xff;
|
||||
macaddr[4] = (macaddr_high >> 8) & 0xff;
|
||||
macaddr[3] = (macaddr_high >> 16) & 0xff;
|
||||
macaddr[2] = (macaddr_high >> 24) & 0xff;
|
||||
macaddr[1] = macaddr_low & 0xff;
|
||||
macaddr[0] = (macaddr_low >> 8) & 0xff;
|
||||
}
|
||||
|
||||
of_update_property(enet_np, newmac);
|
||||
|
||||
put_ocotp_node:
|
||||
of_node_put(ocotp_np);
|
||||
put_enet_node:
|
||||
of_node_put(enet_np);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_HAVE_IMX_GPC
|
||||
int imx_gpc_mf_request_on(unsigned int irq, unsigned int on) { return 0; }
|
||||
EXPORT_SYMBOL_GPL(imx_gpc_mf_request_on);
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SOC_IMX6SL)
|
||||
u32 imx6_lpddr2_freq_change_start, imx6_lpddr2_freq_change_end;
|
||||
void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode) {}
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SOC_IMX6SLL)
|
||||
void imx6sll_lpddr2_freq_change(u32 freq, int bus_freq_mode) {}
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SOC_IMX6SX) && !defined(CONFIG_SOC_IMX6UL)
|
||||
u32 imx6_up_ddr3_freq_change_start, imx6_up_ddr3_freq_change_end;
|
||||
struct imx6_busfreq_info {
|
||||
} __aligned(8);
|
||||
void imx6_up_ddr3_freq_change(struct imx6_busfreq_info *busfreq_info) {}
|
||||
void imx6_up_lpddr2_freq_change(u32 freq, int bus_freq_mode) {}
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SOC_IMX6Q)
|
||||
u32 mx6_ddr3_freq_change_start, mx6_ddr3_freq_change_end;
|
||||
u32 mx6q_lpddr2_freq_change_start, mx6q_lpddr2_freq_change_end;
|
||||
u32 wfe_smp_freq_change_start, wfe_smp_freq_change_end;
|
||||
void mx6_ddr3_freq_change(u32 freq, void *ddr_settings,
|
||||
bool dll_mode, void *iomux_offsets) {}
|
||||
void mx6q_lpddr2_freq_change(u32 freq, void *ddr_settings) {}
|
||||
void wfe_smp_freq_change(u32 cpuid, u32 *ddr_freq_change_done) {}
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_SOC_IMX7D)
|
||||
void imx7_smp_wfe(u32 cpuid, u32 ocram_base) {}
|
||||
void imx7d_ddr3_freq_change(u32 freq) {}
|
||||
#endif
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#define __ASM_ARCH_MXC_COMMON_H__
|
||||
|
||||
#include <linux/reboot.h>
|
||||
#include <soc/imx/src.h>
|
||||
|
||||
struct irq_data;
|
||||
struct platform_device;
|
||||
|
@ -56,9 +57,19 @@ void imx_gpc_set_arm_power_in_lpm(bool power_off);
|
|||
void imx_gpc_set_l2_mem_power_in_lpm(bool power_off);
|
||||
void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw);
|
||||
void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw);
|
||||
void imx_gpcv2_pre_suspend(bool arm_power_off);
|
||||
void imx_gpcv2_post_resume(void);
|
||||
unsigned int imx_gpcv2_is_mf_mix_off(void);
|
||||
void imx_gpcv2_enable_wakeup_for_m4(void);
|
||||
void imx_gpcv2_disable_wakeup_for_m4(void);
|
||||
void imx25_pm_init(void);
|
||||
void imx27_pm_init(void);
|
||||
void imx5_pmu_init(void);
|
||||
#ifdef CONFIG_HAVE_IMX_MU
|
||||
int imx_mu_lpm_ready(bool ready);
|
||||
#else
|
||||
static inline int imx_mu_lpm_ready(bool ready) { return 0; }
|
||||
#endif
|
||||
|
||||
enum mxc_cpu_pwr_mode {
|
||||
WAIT_CLOCKED, /* wfi only */
|
||||
|
@ -89,35 +100,97 @@ void imx_smp_prepare(void);
|
|||
static inline void imx_scu_map_io(void) {}
|
||||
static inline void imx_smp_prepare(void) {}
|
||||
#endif
|
||||
void imx6sx_set_m4_highfreq(bool high_freq);
|
||||
void imx_mu_enable_m4_irqs_in_gic(bool enable);
|
||||
#ifdef CONFIG_HAVE_IMX_GPC
|
||||
void imx_gpc_add_m4_wake_up_irq(u32 irq, bool enable);
|
||||
unsigned int imx_gpc_is_m4_sleeping(void);
|
||||
#else
|
||||
static inline void imx_gpc_add_m4_wake_up_irq(u32 irq, bool enable) {}
|
||||
static inline unsigned int imx_gpc_is_m4_sleeping(void) { return 0; }
|
||||
#endif
|
||||
#ifdef CONFIG_HAVE_IMX_GPCV2
|
||||
int imx_gpcv2_mf_power_on(unsigned int irq, unsigned int on);
|
||||
void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn);
|
||||
void imx_gpcv2_add_m4_wake_up_irq(u32 hwirq, bool enable);
|
||||
#else
|
||||
static inline int imx_gpcv2_mf_power_on(unsigned int irq, unsigned int on) { return 0; }
|
||||
static inline void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn) {}
|
||||
static inline void imx_gpcv2_add_m4_wake_up_irq(u32 hwirq, bool enable) {}
|
||||
#endif
|
||||
void imx_gpc_hold_m4_in_sleep(void);
|
||||
void imx_gpc_release_m4_in_sleep(void);
|
||||
void __init imx_gpcv2_check_dt(void);
|
||||
void imx_gpcv2_set_lpm_mode(enum mxc_cpu_pwr_mode mode);
|
||||
void imx_gpcv2_set_cpu_power_gate_in_idle(bool pdn);
|
||||
void imx_gpcv2_enable_rbc(bool enable);
|
||||
bool imx_mu_is_m4_in_low_freq(void);
|
||||
bool imx_mu_is_m4_in_stop(void);
|
||||
void imx_mu_set_m4_run_mode(void);
|
||||
void imx_src_init(void);
|
||||
void imx_gpc_pre_suspend(bool arm_power_off);
|
||||
void imx_gpc_post_resume(void);
|
||||
void imx_gpc_switch_pupscr_clk(bool flag);
|
||||
void imx_gpc_mask_all(void);
|
||||
void imx_gpc_restore_all(void);
|
||||
void imx_gpc_hwirq_mask(unsigned int hwirq);
|
||||
void imx_gpc_hwirq_unmask(unsigned int hwirq);
|
||||
unsigned int imx_gpc_is_mf_mix_off(void);
|
||||
void imx_anatop_init(void);
|
||||
void imx_anatop_pre_suspend(void);
|
||||
void imx_anatop_post_resume(void);
|
||||
int imx6_set_lpm(enum mxc_cpu_pwr_mode mode);
|
||||
void imx6_set_int_mem_clk_lpm(bool enable);
|
||||
void imx6sl_set_wait_clk(bool enter);
|
||||
void imx6_enet_mac_init(const char *enet_compat, const char *ocotp_compat);
|
||||
void imx6sl_low_power_idle(void);
|
||||
void imx6sll_low_power_idle(void);
|
||||
void imx6sx_low_power_idle(void);
|
||||
void imx6ul_low_power_idle(void);
|
||||
void imx6ull_low_power_idle(void);
|
||||
void imx7d_low_power_idle(void);
|
||||
#ifdef CONFIG_HAVE_IMX_MMDC
|
||||
int imx_mmdc_get_ddr_type(void);
|
||||
int imx_mmdc_get_lpddr2_2ch_mode(void);
|
||||
#else
|
||||
static inline int imx_mmdc_get_ddr_type(void) { return 0; }
|
||||
static inline int imx_mmdc_get_lpddr2_2ch_mode(void) { return 0; }
|
||||
#endif
|
||||
int imx7ulp_set_lpm(enum ulp_cpu_pwr_mode mode);
|
||||
void imx_busfreq_map_io(void);
|
||||
void imx7_pm_map_io(void);
|
||||
void imx6_pm_map_io(void);
|
||||
void imx7ulp_pm_map_io(void);
|
||||
void imx7ulp_enable_nmi(void);
|
||||
void imx7ulp_poweroff(void);
|
||||
|
||||
void imx_cpu_die(unsigned int cpu);
|
||||
int imx_cpu_kill(unsigned int cpu);
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
void v7_cpu_resume(void);
|
||||
void ca7_cpu_resume(void);
|
||||
void imx53_suspend(void __iomem *ocram_vbase);
|
||||
extern const u32 imx53_suspend_sz;
|
||||
void imx6_suspend(void __iomem *ocram_vbase);
|
||||
void imx7_suspend(void __iomem *ocram_vbase);
|
||||
void imx7ulp_cpu_resume(void);
|
||||
void imx7ulp_suspend(void __iomem *ocram_vbase);
|
||||
#else
|
||||
static inline void v7_cpu_resume(void) {}
|
||||
static inline void ca7_cpu_resume(void) {}
|
||||
static inline void imx53_suspend(void __iomem *ocram_vbase) {}
|
||||
static const u32 imx53_suspend_sz;
|
||||
static inline void imx6_suspend(void __iomem *ocram_vbase) {}
|
||||
static inline void imx7_suspend(void __iomem *ocram_vbase) {}
|
||||
static inline void imx7ulp_cpu_resume(void) {}
|
||||
static inline void imx7ulp_suspend(void __iomem *ocram_vbase) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAVE_IMX_DDRC
|
||||
int imx_ddrc_get_ddr_type(void);
|
||||
#else
|
||||
static inline int imx_ddrc_get_ddr_type(void) { return 0; }
|
||||
#endif
|
||||
|
||||
void imx6_pm_ccm_init(const char *ccm_compat);
|
||||
|
@ -126,6 +199,7 @@ void imx6dl_pm_init(void);
|
|||
void imx6sl_pm_init(void);
|
||||
void imx6sx_pm_init(void);
|
||||
void imx6ul_pm_init(void);
|
||||
void imx7d_pm_init(void);
|
||||
void imx7ulp_pm_init(void);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -151,4 +225,5 @@ static inline void imx_init_l2cache(void) {}
|
|||
extern const struct smp_operations imx_smp_ops;
|
||||
extern const struct smp_operations ls1021a_smp_ops;
|
||||
|
||||
extern bool uart_from_osc;
|
||||
#endif
|
||||
|
|
|
@ -1,26 +1,104 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017-2018 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/busfreq-imx.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/psci.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/proc-fns.h>
|
||||
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define MAX_MMDC_IO_NUM 19
|
||||
|
||||
static void __iomem *wfi_iram_base;
|
||||
extern unsigned long iram_tlb_base_addr;
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
extern unsigned long mx6sl_lpm_wfi_start asm("mx6sl_lpm_wfi_start");
|
||||
extern unsigned long mx6sl_lpm_wfi_end asm("mx6sl_lpm_wfi_end");
|
||||
#endif
|
||||
|
||||
struct imx6_cpuidle_pm_info {
|
||||
u32 pm_info_size; /* Size of pm_info */
|
||||
u32 ttbr;
|
||||
void __iomem *mmdc_base;
|
||||
void __iomem *iomuxc_base;
|
||||
void __iomem *ccm_base;
|
||||
void __iomem *l2_base;
|
||||
void __iomem *anatop_base;
|
||||
u32 mmdc_io_num; /*Number of MMDC IOs which need saved/restored. */
|
||||
u32 mmdc_io_val[MAX_MMDC_IO_NUM][2]; /* To save offset and value */
|
||||
} __aligned(8);
|
||||
|
||||
static const u32 imx6sl_mmdc_io_offset[] __initconst = {
|
||||
0x30c, 0x310, 0x314, 0x318, /* DQM0 ~ DQM3 */
|
||||
0x5c4, 0x5cc, 0x5d4, 0x5d8, /* GPR_B0DS ~ GPR_B3DS */
|
||||
0x300, 0x31c, 0x338, 0x5ac, /*CAS, RAS, SDCLK_0, GPR_ADDS */
|
||||
0x33c, 0x340, 0x5b0, 0x5c0, /*SODT0, SODT1, ,MODE_CTL, MODE */
|
||||
0x330, 0x334, 0x320, /*SDCKE0, SDCK1, RESET */
|
||||
};
|
||||
|
||||
static struct regulator *vbus_ldo;
|
||||
static struct regulator_dev *ldo2p5_dummy_regulator_rdev;
|
||||
static struct regulator_init_data ldo2p5_dummy_initdata = {
|
||||
.constraints = {
|
||||
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
|
||||
},
|
||||
};
|
||||
static int ldo2p5_dummy_enable;
|
||||
|
||||
static void (*imx6sl_wfi_in_iram_fn)(void __iomem *iram_vbase,
|
||||
int audio_mode, bool vbus_ldo);
|
||||
|
||||
#define MX6SL_POWERDWN_IDLE_PARAM \
|
||||
((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
static int imx6sl_enter_wait(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
imx6_set_lpm(WAIT_UNCLOCKED);
|
||||
/*
|
||||
* Software workaround for ERR005311, see function
|
||||
* description for details.
|
||||
*/
|
||||
imx6sl_set_wait_clk(true);
|
||||
cpu_do_idle();
|
||||
imx6sl_set_wait_clk(false);
|
||||
|
||||
if ((mode == BUS_FREQ_AUDIO) || (mode == BUS_FREQ_ULTRA_LOW)) {
|
||||
/*
|
||||
* bit 2 used for low power mode;
|
||||
* bit 1 used for the ldo2p5_dummmy enable
|
||||
*/
|
||||
if (psci_ops.cpu_suspend) {
|
||||
psci_ops.cpu_suspend((MX6SL_POWERDWN_IDLE_PARAM | ((mode == BUS_FREQ_AUDIO ? 1 : 0) << 2) |
|
||||
(ldo2p5_dummy_enable ? 1 : 0) << 1), __pa(cpu_resume));
|
||||
} else {
|
||||
imx6sl_wfi_in_iram_fn(wfi_iram_base, (mode == BUS_FREQ_AUDIO) ? 1 : 0,
|
||||
ldo2p5_dummy_enable);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Software workaround for ERR005311, see function
|
||||
* description for details.
|
||||
*/
|
||||
imx6sl_set_wait_clk(true);
|
||||
cpu_do_idle();
|
||||
imx6sl_set_wait_clk(false);
|
||||
}
|
||||
imx6_set_lpm(WAIT_CLOCKED);
|
||||
|
||||
return index;
|
||||
|
@ -48,5 +126,109 @@ static struct cpuidle_driver imx6sl_cpuidle_driver = {
|
|||
|
||||
int __init imx6sl_cpuidle_init(void)
|
||||
{
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct imx6_cpuidle_pm_info *pm_info;
|
||||
int i;
|
||||
const u32 *mmdc_offset_array;
|
||||
u32 wfi_code_size;
|
||||
|
||||
vbus_ldo = regulator_get(NULL, "ldo2p5-dummy");
|
||||
if (IS_ERR(vbus_ldo))
|
||||
vbus_ldo = NULL;
|
||||
|
||||
wfi_iram_base = (void *)(iram_tlb_base_addr + MX6_CPUIDLE_IRAM_ADDR_OFFSET);
|
||||
|
||||
/* Make sure wif_iram_base is 8 byte aligned. */
|
||||
if ((uintptr_t)(wfi_iram_base) & (FNCPY_ALIGN - 1))
|
||||
wfi_iram_base += FNCPY_ALIGN - ((uintptr_t)wfi_iram_base % (FNCPY_ALIGN));
|
||||
|
||||
pm_info = wfi_iram_base;
|
||||
pm_info->pm_info_size = sizeof(*pm_info);
|
||||
pm_info->mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset);
|
||||
mmdc_offset_array = imx6sl_mmdc_io_offset;
|
||||
pm_info->mmdc_base = (void __iomem *)IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR);
|
||||
pm_info->ccm_base = (void __iomem *)IMX_IO_P2V(MX6Q_CCM_BASE_ADDR);
|
||||
pm_info->anatop_base = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
|
||||
pm_info->iomuxc_base = (void __iomem *)IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR);
|
||||
pm_info->l2_base = (void __iomem *)IMX_IO_P2V(MX6Q_L2_BASE_ADDR);
|
||||
|
||||
/* Only save mmdc io offset, settings will be saved in asm code */
|
||||
for (i = 0; i < pm_info->mmdc_io_num; i++)
|
||||
pm_info->mmdc_io_val[i][0] = mmdc_offset_array[i];
|
||||
|
||||
/* calculate the wfi code size */
|
||||
wfi_code_size = (&mx6sl_lpm_wfi_end -&mx6sl_lpm_wfi_start) *4;
|
||||
|
||||
imx6sl_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base + sizeof(*pm_info),
|
||||
&imx6sl_low_power_idle, wfi_code_size);
|
||||
#endif
|
||||
|
||||
return cpuidle_register(&imx6sl_cpuidle_driver, NULL);
|
||||
}
|
||||
|
||||
static int imx_ldo2p5_dummy_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
ldo2p5_dummy_enable = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_ldo2p5_dummy_disable(struct regulator_dev *rdev)
|
||||
{
|
||||
ldo2p5_dummy_enable = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_ldo2p5_dummy_is_enable(struct regulator_dev *rdev)
|
||||
{
|
||||
return ldo2p5_dummy_enable;
|
||||
}
|
||||
|
||||
static struct regulator_ops ldo2p5_dummy_ops = {
|
||||
.enable = imx_ldo2p5_dummy_enable,
|
||||
.disable = imx_ldo2p5_dummy_disable,
|
||||
.is_enabled = imx_ldo2p5_dummy_is_enable,
|
||||
};
|
||||
|
||||
static struct regulator_desc ldo2p5_dummy_desc = {
|
||||
.name = "ldo2p5-dummy",
|
||||
.id = -1,
|
||||
.type = REGULATOR_VOLTAGE,
|
||||
.owner = THIS_MODULE,
|
||||
.ops = &ldo2p5_dummy_ops,
|
||||
};
|
||||
|
||||
static int ldo2p5_dummy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regulator_config config = { };
|
||||
int ret;
|
||||
|
||||
config.dev = &pdev->dev;
|
||||
config.init_data = &ldo2p5_dummy_initdata;
|
||||
config.of_node = pdev->dev.of_node;
|
||||
|
||||
ldo2p5_dummy_regulator_rdev = regulator_register(&ldo2p5_dummy_desc, &config);
|
||||
if (IS_ERR(ldo2p5_dummy_regulator_rdev)) {
|
||||
ret = PTR_ERR(ldo2p5_dummy_regulator_rdev);
|
||||
dev_err(&pdev->dev, "Failed to register dummy ldo2p5 regulator: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_ldo2p5_dummy_ids[] = {
|
||||
{ .compatible = "fsl,imx6-dummy-ldo2p5", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ofm, imx_ldo2p5_dummy_ids);
|
||||
|
||||
static struct platform_driver ldo2p5_dummy_driver = {
|
||||
.probe = ldo2p5_dummy_probe,
|
||||
.driver = {
|
||||
.name = "ldo2p5-dummy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = imx_ldo2p5_dummy_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ldo2p5_dummy_driver);
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017 NXP.
|
||||
*
|
||||
* 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/busfreq-imx.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/psci.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define MAX_MMDC_IO_NUM 14
|
||||
|
||||
#define PMU_LOW_PWR_CTRL 0x270
|
||||
#define XTALOSC24M_OSC_CONFIG0 0x2a0
|
||||
#define XTALOSC24M_OSC_CONFIG1 0x2b0
|
||||
#define XTALOSC24M_OSC_CONFIG2 0x2c0
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT 24
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT 16
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT 12
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT 4
|
||||
#define XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT 1
|
||||
#define XTALOSC24M_OSC_CONFIG0_START_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT 20
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK 0xfff
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT 0
|
||||
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
static void __iomem *wfi_iram_base;
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
static void __iomem *wfi_iram_base_phys;
|
||||
extern unsigned long mx6sll_lpm_wfi_start asm("mx6sll_lpm_wfi_start");
|
||||
extern unsigned long mx6sll_lpm_wfi_end asm("mx6sll_lpm_wfi_end");
|
||||
#endif
|
||||
|
||||
struct imx6_pm_base {
|
||||
phys_addr_t pbase;
|
||||
void __iomem *vbase;
|
||||
};
|
||||
|
||||
struct imx6_cpuidle_pm_info {
|
||||
phys_addr_t pbase; /* The physical address of pm_info. */
|
||||
phys_addr_t resume_addr; /* The physical resume address for asm code */
|
||||
u32 pm_info_size; /* Size of pm_info. */
|
||||
u32 ttbr;
|
||||
struct imx6_pm_base mmdc_base;
|
||||
struct imx6_pm_base iomuxc_base;
|
||||
struct imx6_pm_base ccm_base;
|
||||
struct imx6_pm_base gpc_base;
|
||||
struct imx6_pm_base anatop_base;
|
||||
struct imx6_pm_base src_base;
|
||||
struct imx6_pm_base l2_base;
|
||||
u32 saved_diagnostic; /* To save disagnostic register */
|
||||
u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
|
||||
u32 mmdc_io_val[MAX_MMDC_IO_NUM][2]; /* To save offset and value */
|
||||
} __aligned(8);
|
||||
|
||||
static const u32 imx6sll_mmdc_io_offset[] __initconst = {
|
||||
0x294, 0x298, 0x29c, 0x2a0, /* DQM0, DQM1, RAS, CAS */
|
||||
0x544, 0x54c, 0x554, 0x558, /* GPR_B0DS ~ GPR_B3DS */
|
||||
0x530, 0x540, 0x2ac, 0x52c, /* MODE_CTL, MODE, SDCLK0, GPR_ADDS */
|
||||
0x2a4, 0x2a8, /* SDCKE0, SDCKE1 */
|
||||
};
|
||||
|
||||
static void (*imx6sll_wfi_in_iram_fn)(void __iomem *iram_vbase);
|
||||
|
||||
#define MX6SLL_POWERDWN_IDLE_PARAM \
|
||||
((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
static int imx6sll_idle_finish(unsigned long val)
|
||||
{
|
||||
if (psci_ops.cpu_suspend)
|
||||
psci_ops.cpu_suspend(MX6SLL_POWERDWN_IDLE_PARAM,
|
||||
__pa(cpu_resume));
|
||||
else
|
||||
imx6sll_wfi_in_iram_fn(wfi_iram_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx6sll_enter_wait(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
imx6_set_lpm(WAIT_UNCLOCKED);
|
||||
if ((index == 1) || ((mode != BUS_FREQ_LOW) && index == 2)) {
|
||||
index = 1;
|
||||
cpu_do_idle();
|
||||
} else {
|
||||
imx_gpc_switch_pupscr_clk(true);
|
||||
/* Need to notify there is a cpu pm operation. */
|
||||
cpu_pm_enter();
|
||||
cpu_cluster_pm_enter();
|
||||
|
||||
cpu_suspend(0, imx6sll_idle_finish);
|
||||
|
||||
cpu_cluster_pm_exit();
|
||||
cpu_pm_exit();
|
||||
imx6_enable_rbc(false);
|
||||
|
||||
imx_gpc_switch_pupscr_clk(false);
|
||||
}
|
||||
|
||||
imx6_set_lpm(WAIT_CLOCKED);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver imx6sll_cpuidle_driver = {
|
||||
.name = "imx6sll_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
/* WFI */
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
/* WAIT */
|
||||
{
|
||||
.exit_latency = 50,
|
||||
.target_residency = 75,
|
||||
.enter = imx6sll_enter_wait,
|
||||
.name = "WAIT",
|
||||
.desc = "Clock off",
|
||||
},
|
||||
/* LOW POWER IDLE */
|
||||
{
|
||||
/*
|
||||
* RBC 130us + ARM gating 43us + RBC clear 65us
|
||||
* + PLL2 relock 450us and some margin, here set
|
||||
* it to 700us.
|
||||
*/
|
||||
.exit_latency = 700,
|
||||
.target_residency = 1000,
|
||||
.enter = imx6sll_enter_wait,
|
||||
.name = "LOW-POWER-IDLE",
|
||||
.desc = "ARM power off",
|
||||
}
|
||||
},
|
||||
.state_count = 3,
|
||||
.safe_state_index = 0,
|
||||
};
|
||||
|
||||
int __init imx6sll_cpuidle_init(void)
|
||||
{
|
||||
void __iomem *anatop_base = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
|
||||
u32 val;
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct imx6_cpuidle_pm_info *cpuidle_pm_info;
|
||||
int i;
|
||||
const u32 *mmdc_offset_array;
|
||||
u32 wfi_code_size;
|
||||
|
||||
wfi_iram_base_phys = (void *)(iram_tlb_phys_addr + MX6_CPUIDLE_IRAM_ADDR_OFFSET);
|
||||
|
||||
/* Make sure wfi_iram_base is 8 byte aligned. */
|
||||
if ((uintptr_t)(wfi_iram_base_phys) & (FNCPY_ALIGN - 1))
|
||||
wfi_iram_base_phys += FNCPY_ALIGN - ((uintptr_t)wfi_iram_base_phys % (FNCPY_ALIGN));
|
||||
|
||||
wfi_iram_base = (void *)IMX_IO_P2V((unsigned long) wfi_iram_base_phys);
|
||||
|
||||
cpuidle_pm_info = wfi_iram_base;
|
||||
cpuidle_pm_info->pbase = (phys_addr_t) wfi_iram_base_phys;
|
||||
cpuidle_pm_info->pm_info_size = sizeof(*cpuidle_pm_info);
|
||||
cpuidle_pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
|
||||
cpuidle_pm_info->mmdc_io_num = ARRAY_SIZE(imx6sll_mmdc_io_offset);
|
||||
mmdc_offset_array = imx6sll_mmdc_io_offset;
|
||||
|
||||
cpuidle_pm_info->mmdc_base.pbase = MX6Q_MMDC_P0_BASE_ADDR;
|
||||
cpuidle_pm_info->mmdc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->ccm_base.pbase = MX6Q_CCM_BASE_ADDR;
|
||||
cpuidle_pm_info->ccm_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_CCM_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->anatop_base.pbase = MX6Q_ANATOP_BASE_ADDR;
|
||||
cpuidle_pm_info->anatop_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->gpc_base.pbase = MX6Q_GPC_BASE_ADDR;
|
||||
cpuidle_pm_info->gpc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_GPC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->iomuxc_base.pbase = MX6Q_IOMUXC_BASE_ADDR;
|
||||
cpuidle_pm_info->iomuxc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->src_base.pbase = MX6Q_SRC_BASE_ADDR;
|
||||
cpuidle_pm_info->src_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_SRC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->l2_base.pbase = MX6Q_L2_BASE_ADDR;
|
||||
cpuidle_pm_info->l2_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_L2_BASE_ADDR);
|
||||
|
||||
/* Only save mmdc io offset, settings will be saved in asm code */
|
||||
for (i = 0; i < cpuidle_pm_info->mmdc_io_num; i++)
|
||||
cpuidle_pm_info->mmdc_io_val[i][0] = mmdc_offset_array[i];
|
||||
|
||||
wfi_code_size = (&mx6sll_lpm_wfi_end -&mx6sll_lpm_wfi_start) *4;
|
||||
|
||||
imx6sll_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base + sizeof(*cpuidle_pm_info),
|
||||
&imx6sll_low_power_idle, wfi_code_size);
|
||||
#endif
|
||||
|
||||
imx6_set_int_mem_clk_lpm(true);
|
||||
|
||||
/*
|
||||
* enable RC-OSC here, as it needs at least 4ms for RC-OSC to
|
||||
* be stable, low power idle flow can NOT endure this big
|
||||
* latency, so we make RC-OSC self-tuning enabled here.
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + PMU_LOW_PWR_CTRL);
|
||||
val |= 0x1;
|
||||
writel_relaxed(val, anatop_base + PMU_LOW_PWR_CTRL);
|
||||
/*
|
||||
* config RC-OSC freq
|
||||
* tune_enable = 1;tune_start = 1;hyst_plus = 0;hyst_minus = 0;
|
||||
* osc_prog = 0xa7;
|
||||
*/
|
||||
writel_relaxed(
|
||||
0x4 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT |
|
||||
0xa7 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_START_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set count_trg = 0x2dc */
|
||||
writel_relaxed(
|
||||
0x40 << XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT |
|
||||
0x2dc << XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
/* wait 4ms according to hardware design */
|
||||
msleep(4);
|
||||
/*
|
||||
* now add some hysteresis, hyst_plus=3, hyst_minus=3
|
||||
* (the minimum hysteresis that looks good is 2)
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
val &= ~((XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT));
|
||||
val |= (0x3 << XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(0x3 << XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT);
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set the count_1m_trg = 0x2d7 */
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
val &= ~(XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT);
|
||||
val |= 0x2d7 << XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT;
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
/*
|
||||
* hardware design require to write XTALOSC24M_OSC_CONFIG0 or
|
||||
* XTALOSC24M_OSC_CONFIG1 to
|
||||
* make XTALOSC24M_OSC_CONFIG2 write work
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
|
||||
return cpuidle_register(&imx6sll_cpuidle_driver, NULL);
|
||||
}
|
|
@ -1,20 +1,97 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2014 Freescale Semiconductor, Inc.
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/busfreq-imx.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/psci.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/tlb.h>
|
||||
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
#include "hardware.h"
|
||||
|
||||
static int imx6sx_idle_finish(unsigned long val)
|
||||
#define MX6_MAX_MMDC_IO_NUM 19
|
||||
|
||||
#define PMU_LOW_PWR_CTRL 0x270
|
||||
#define XTALOSC24M_OSC_CONFIG0 0x2a0
|
||||
#define XTALOSC24M_OSC_CONFIG1 0x2b0
|
||||
#define XTALOSC24M_OSC_CONFIG2 0x2c0
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT 24
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT 16
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT 12
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT 4
|
||||
#define XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT 1
|
||||
#define XTALOSC24M_OSC_CONFIG0_START_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT 20
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK 0xfff
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT 0
|
||||
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
|
||||
static void __iomem *wfi_iram_base;
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
static void __iomem *wfi_iram_base_phys;
|
||||
extern unsigned long mx6sx_lpm_wfi_start asm("mx6sx_lpm_wfi_start");
|
||||
extern unsigned long mx6sx_lpm_wfi_end asm("mx6sx_lpm_wfi_end");
|
||||
#endif
|
||||
|
||||
struct imx6_pm_base {
|
||||
phys_addr_t pbase;
|
||||
void __iomem *vbase;
|
||||
};
|
||||
|
||||
static const u32 imx6sx_mmdc_io_offset[] __initconst = {
|
||||
0x2ec, 0x2f0, 0x2f4, 0x2f8, /* DQM0 ~ DQM3 */
|
||||
0x330, 0x334, 0x338, 0x33c, /* SDQS0 ~ SDQS3 */
|
||||
0x60c, 0x610, 0x61c, 0x620, /* B0DS ~ B3DS */
|
||||
0x5f8, 0x608, 0x310, 0x314, /* CTL, MODE, SODT0, SODT1 */
|
||||
0x300, 0x2fc, 0x32c, /* CAS, RAS, SDCLK_0 */
|
||||
};
|
||||
|
||||
struct imx6_cpuidle_pm_info {
|
||||
phys_addr_t pbase; /* The physical address of pm_info. */
|
||||
phys_addr_t resume_addr; /* The physical resume address for asm code */
|
||||
u32 pm_info_size; /* Size of pm_info. */
|
||||
u32 ttbr;
|
||||
struct imx6_pm_base mmdc_base;
|
||||
struct imx6_pm_base iomuxc_base;
|
||||
struct imx6_pm_base ccm_base;
|
||||
struct imx6_pm_base gpc_base;
|
||||
struct imx6_pm_base l2_base;
|
||||
struct imx6_pm_base anatop_base;
|
||||
struct imx6_pm_base src_base;
|
||||
struct imx6_pm_base sema4_base;
|
||||
u32 saved_diagnostic; /* To save disagnostic register */
|
||||
u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
|
||||
u32 mmdc_io_val[MX6_MAX_MMDC_IO_NUM][2]; /* To save offset and value */
|
||||
} __aligned(8);
|
||||
|
||||
static void (*imx6sx_wfi_in_iram_fn)(void __iomem *iram_vbase);
|
||||
|
||||
#define MX6SX_POWERDWN_IDLE_PARAM \
|
||||
((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
static int imx6_idle_finish(unsigned long val)
|
||||
{
|
||||
/*
|
||||
* for Cortex-A7 which has an internal L2
|
||||
|
@ -25,7 +102,11 @@ static int imx6sx_idle_finish(unsigned long val)
|
|||
* just call flush_cache_all() is fine.
|
||||
*/
|
||||
flush_cache_all();
|
||||
cpu_do_idle();
|
||||
if (psci_ops.cpu_suspend)
|
||||
psci_ops.cpu_suspend(MX6SX_POWERDWN_IDLE_PARAM,
|
||||
__pa(cpu_resume));
|
||||
else
|
||||
imx6sx_wfi_in_iram_fn(wfi_iram_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -33,29 +114,22 @@ static int imx6sx_idle_finish(unsigned long val)
|
|||
static int imx6sx_enter_wait(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
imx6_set_lpm(WAIT_UNCLOCKED);
|
||||
|
||||
switch (index) {
|
||||
case 1:
|
||||
if ((index == 1) || ((mode != BUS_FREQ_LOW) && index == 2)) {
|
||||
index = 1;
|
||||
cpu_do_idle();
|
||||
break;
|
||||
case 2:
|
||||
imx6_enable_rbc(true);
|
||||
imx_gpc_set_arm_power_in_lpm(true);
|
||||
imx_set_cpu_jump(0, v7_cpu_resume);
|
||||
/* Need to notify there is a cpu pm operation. */
|
||||
cpu_pm_enter();
|
||||
cpu_cluster_pm_enter();
|
||||
} else {
|
||||
/* Need to notify there is a cpu pm operation. */
|
||||
cpu_pm_enter();
|
||||
cpu_cluster_pm_enter();
|
||||
|
||||
cpu_suspend(0, imx6sx_idle_finish);
|
||||
cpu_suspend(0, imx6_idle_finish);
|
||||
|
||||
cpu_cluster_pm_exit();
|
||||
cpu_pm_exit();
|
||||
imx_gpc_set_arm_power_in_lpm(false);
|
||||
imx6_enable_rbc(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
cpu_cluster_pm_exit();
|
||||
cpu_pm_exit();
|
||||
imx6_enable_rbc(false);
|
||||
}
|
||||
|
||||
imx6_set_lpm(WAIT_CLOCKED);
|
||||
|
@ -69,25 +143,23 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
|
|||
.states = {
|
||||
/* WFI */
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
/* WAIT */
|
||||
/* WAIT MODE */
|
||||
{
|
||||
.exit_latency = 50,
|
||||
.target_residency = 75,
|
||||
.flags = CPUIDLE_FLAG_TIMER_STOP,
|
||||
.enter = imx6sx_enter_wait,
|
||||
.name = "WAIT",
|
||||
.desc = "Clock off",
|
||||
},
|
||||
/* WAIT + ARM power off */
|
||||
/* LOW POWER IDLE */
|
||||
{
|
||||
/*
|
||||
* ARM gating 31us * 5 + RBC clear 65us
|
||||
* and some margin for SW execution, here set it
|
||||
* to 300us.
|
||||
* RBC 130us + ARM gating 93us + RBC clear 65us
|
||||
* + PLL2 relock 450us and some margin, here set
|
||||
* it to 800us.
|
||||
*/
|
||||
.exit_latency = 300,
|
||||
.target_residency = 500,
|
||||
.flags = CPUIDLE_FLAG_TIMER_STOP,
|
||||
.exit_latency = 800,
|
||||
.target_residency = 1000,
|
||||
.enter = imx6sx_enter_wait,
|
||||
.name = "LOW-POWER-IDLE",
|
||||
.desc = "ARM power off",
|
||||
|
@ -99,17 +171,119 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
|
|||
|
||||
int __init imx6sx_cpuidle_init(void)
|
||||
{
|
||||
void __iomem *anatop_base = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
|
||||
u32 val;
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct imx6_cpuidle_pm_info *cpuidle_pm_info;
|
||||
int i;
|
||||
const u32 *mmdc_offset_array;
|
||||
u32 wfi_code_size;
|
||||
|
||||
wfi_iram_base_phys = (void *)(iram_tlb_phys_addr + MX6_CPUIDLE_IRAM_ADDR_OFFSET);
|
||||
|
||||
/* Make sure wfi_iram_base is 8 byte aligned. */
|
||||
if ((uintptr_t)(wfi_iram_base_phys) & (FNCPY_ALIGN - 1))
|
||||
wfi_iram_base_phys += FNCPY_ALIGN - ((uintptr_t)wfi_iram_base_phys % (FNCPY_ALIGN));
|
||||
|
||||
wfi_iram_base = (void *)IMX_IO_P2V((unsigned long) wfi_iram_base_phys);
|
||||
|
||||
cpuidle_pm_info = wfi_iram_base;
|
||||
cpuidle_pm_info->pbase = (phys_addr_t) wfi_iram_base_phys;
|
||||
cpuidle_pm_info->pm_info_size = sizeof(*cpuidle_pm_info);
|
||||
cpuidle_pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
|
||||
cpuidle_pm_info->mmdc_io_num = ARRAY_SIZE(imx6sx_mmdc_io_offset);
|
||||
mmdc_offset_array = imx6sx_mmdc_io_offset;
|
||||
|
||||
cpuidle_pm_info->mmdc_base.pbase = MX6Q_MMDC_P0_BASE_ADDR;
|
||||
cpuidle_pm_info->mmdc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->ccm_base.pbase = MX6Q_CCM_BASE_ADDR;
|
||||
cpuidle_pm_info->ccm_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_CCM_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->anatop_base.pbase = MX6Q_ANATOP_BASE_ADDR;
|
||||
cpuidle_pm_info->anatop_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->gpc_base.pbase = MX6Q_GPC_BASE_ADDR;
|
||||
cpuidle_pm_info->gpc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_GPC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->iomuxc_base.pbase = MX6Q_IOMUXC_BASE_ADDR;
|
||||
cpuidle_pm_info->iomuxc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->l2_base.pbase = MX6Q_L2_BASE_ADDR;
|
||||
cpuidle_pm_info->l2_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_L2_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->src_base.pbase = MX6Q_SRC_BASE_ADDR;
|
||||
cpuidle_pm_info->src_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_SRC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->sema4_base.pbase = MX6Q_SEMA4_BASE_ADDR;
|
||||
cpuidle_pm_info->sema4_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX6Q_SEMA4_BASE_ADDR);
|
||||
|
||||
/* only save mmdc io offset, settings will be saved in asm code */
|
||||
for (i = 0; i < cpuidle_pm_info->mmdc_io_num; i++)
|
||||
cpuidle_pm_info->mmdc_io_val[i][0] = mmdc_offset_array[i];
|
||||
|
||||
/* code size should include cpuidle_pm_info size */
|
||||
wfi_code_size = (&mx6sx_lpm_wfi_end -&mx6sx_lpm_wfi_start) *4 + sizeof(*cpuidle_pm_info);
|
||||
imx6sx_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base + sizeof(*cpuidle_pm_info),
|
||||
&imx6sx_low_power_idle, wfi_code_size);
|
||||
#endif
|
||||
|
||||
imx6_set_int_mem_clk_lpm(true);
|
||||
imx6_enable_rbc(false);
|
||||
imx_gpc_set_l2_mem_power_in_lpm(false);
|
||||
/*
|
||||
* set ARM power up/down timing to the fastest,
|
||||
* sw2iso and sw can be set to one 32K cycle = 31us
|
||||
* except for power up sw2iso which need to be
|
||||
* larger than LDO ramp up time.
|
||||
*/
|
||||
imx_gpc_set_arm_power_up_timing(cpu_is_imx6sx() ? 0xf : 0x2, 1);
|
||||
imx_gpc_set_arm_power_down_timing(1, 1);
|
||||
|
||||
if (imx_get_soc_revision() >= IMX_CHIP_REVISION_1_2) {
|
||||
/*
|
||||
* enable RC-OSC here, as it needs at least 4ms for RC-OSC to
|
||||
* be stable, low power idle flow can NOT endure this big
|
||||
* latency, so we make RC-OSC self-tuning enabled here.
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + PMU_LOW_PWR_CTRL);
|
||||
val |= 0x1;
|
||||
writel_relaxed(val, anatop_base + PMU_LOW_PWR_CTRL);
|
||||
/*
|
||||
* config RC-OSC freq
|
||||
* tune_enable = 1;tune_start = 1;hyst_plus = 0;hyst_minus = 0;
|
||||
* osc_prog = 0xa7;
|
||||
*/
|
||||
writel_relaxed(
|
||||
0x4 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT |
|
||||
0xa7 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_START_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set count_trg = 0x2dc */
|
||||
writel_relaxed(
|
||||
0x40 << XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT |
|
||||
0x2dc << XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
/* wait 4ms according to hardware design */
|
||||
msleep(4);
|
||||
/*
|
||||
* now add some hysteresis, hyst_plus=3, hyst_minus=3
|
||||
* (the minimum hysteresis that looks good is 2)
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
val &= ~((XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT));
|
||||
val |= (0x3 << XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(0x3 << XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT);
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set the count_1m_trg = 0x2d7 */
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
val &= ~(XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT);
|
||||
val |= 0x2d7 << XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT;
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
/*
|
||||
* hardware design require to write XTALOSC24M_OSC_CONFIG0 or
|
||||
* XTALOSC24M_OSC_CONFIG1 to
|
||||
* make XTALOSC24M_OSC_CONFIG2 write work
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
}
|
||||
|
||||
return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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/busfreq-imx.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/psci.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define MAX_MMDC_IO_NUM 14
|
||||
|
||||
#define PMU_LOW_PWR_CTRL 0x270
|
||||
#define XTALOSC24M_OSC_CONFIG0 0x2a0
|
||||
#define XTALOSC24M_OSC_CONFIG1 0x2b0
|
||||
#define XTALOSC24M_OSC_CONFIG2 0x2c0
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT 24
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT 16
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT 12
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT 4
|
||||
#define XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT 1
|
||||
#define XTALOSC24M_OSC_CONFIG0_START_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT 20
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK 0xfff
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT 0
|
||||
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
static void __iomem *wfi_iram_base;
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
static void __iomem *wfi_iram_base_phys;
|
||||
extern unsigned long mx6ul_lpm_wfi_start asm("mx6ul_lpm_wfi_start");
|
||||
extern unsigned long mx6ul_lpm_wfi_end asm("mx6ul_lpm_wfi_end");
|
||||
extern unsigned long mx6ull_lpm_wfi_start asm("mx6ull_lpm_wfi_start");
|
||||
extern unsigned long mx6ull_lpm_wfi_end asm("mx6ull_lpm_wfi_end");
|
||||
#endif
|
||||
|
||||
struct imx6_pm_base {
|
||||
phys_addr_t pbase;
|
||||
void __iomem *vbase;
|
||||
};
|
||||
|
||||
struct imx6_cpuidle_pm_info {
|
||||
phys_addr_t pbase; /* The physical address of pm_info. */
|
||||
phys_addr_t resume_addr; /* The physical resume address for asm code */
|
||||
u32 pm_info_size; /* Size of pm_info. */
|
||||
u32 ttbr;
|
||||
struct imx6_pm_base mmdc_base;
|
||||
struct imx6_pm_base iomuxc_base;
|
||||
struct imx6_pm_base ccm_base;
|
||||
struct imx6_pm_base gpc_base;
|
||||
struct imx6_pm_base anatop_base;
|
||||
struct imx6_pm_base src_base;
|
||||
u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
|
||||
u32 mmdc_io_val[MAX_MMDC_IO_NUM][2]; /* To save offset and value */
|
||||
} __aligned(8);
|
||||
|
||||
static const u32 imx6ul_mmdc_io_offset[] __initconst = {
|
||||
0x244, 0x248, 0x24c, 0x250, /* DQM0, DQM1, RAS, CAS */
|
||||
0x27c, 0x498, 0x4a4, 0x490, /* SDCLK0, GPR_B0DS-B1DS, GPR_ADDS */
|
||||
0x280, 0x284, 0x260, 0x264, /* SDQS0~1, SODT0, SODT1 */
|
||||
0x494, 0x4b0, /* MODE_CTL, MODE, */
|
||||
};
|
||||
|
||||
static void (*imx6ul_wfi_in_iram_fn)(void __iomem *iram_vbase);
|
||||
|
||||
#define MX6UL_POWERDWN_IDLE_PARAM \
|
||||
((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
static int imx6ul_idle_finish(unsigned long val)
|
||||
{
|
||||
if (psci_ops.cpu_suspend)
|
||||
psci_ops.cpu_suspend(MX6UL_POWERDWN_IDLE_PARAM,
|
||||
__pa(cpu_resume));
|
||||
else
|
||||
imx6ul_wfi_in_iram_fn(wfi_iram_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx6ul_enter_wait(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
imx6_set_lpm(WAIT_UNCLOCKED);
|
||||
if ((index == 1) || ((mode != BUS_FREQ_LOW) && index == 2)) {
|
||||
cpu_do_idle();
|
||||
index = 1;
|
||||
} else {
|
||||
/*
|
||||
* i.MX6UL TO1.0 ARM power up uses IPG/2048 as clock source,
|
||||
* from TO1.1, PGC_CPU_PUPSCR bit [5] is re-defined to switch
|
||||
* clock to IPG/32, enable this bit to speed up the ARM power
|
||||
* up process in low power idle case.
|
||||
*/
|
||||
if (cpu_is_imx6ul() && imx_get_soc_revision() >
|
||||
IMX_CHIP_REVISION_1_0)
|
||||
imx_gpc_switch_pupscr_clk(true);
|
||||
/* Need to notify there is a cpu pm operation. */
|
||||
cpu_pm_enter();
|
||||
cpu_cluster_pm_enter();
|
||||
|
||||
cpu_suspend(0, imx6ul_idle_finish);
|
||||
|
||||
cpu_cluster_pm_exit();
|
||||
cpu_pm_exit();
|
||||
imx6_enable_rbc(false);
|
||||
|
||||
if (cpu_is_imx6ul() && imx_get_soc_revision() >
|
||||
IMX_CHIP_REVISION_1_0)
|
||||
imx_gpc_switch_pupscr_clk(false);
|
||||
}
|
||||
|
||||
imx6_set_lpm(WAIT_CLOCKED);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver imx6ul_cpuidle_driver_v2 = {
|
||||
.name = "imx6ul_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
/* WFI */
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
/* WAIT */
|
||||
{
|
||||
.exit_latency = 50,
|
||||
.target_residency = 75,
|
||||
.enter = imx6ul_enter_wait,
|
||||
.name = "WAIT",
|
||||
.desc = "Clock off",
|
||||
},
|
||||
/* LOW POWER IDLE */
|
||||
{
|
||||
/*
|
||||
* RBC 130us + ARM gating 43us + RBC clear 65us
|
||||
* + PLL2 relock 450us and some margin, here set
|
||||
* it to 700us.
|
||||
*/
|
||||
.exit_latency = 700,
|
||||
.target_residency = 1000,
|
||||
.enter = imx6ul_enter_wait,
|
||||
.name = "LOW-POWER-IDLE",
|
||||
.desc = "ARM power off",
|
||||
}
|
||||
},
|
||||
.state_count = 3,
|
||||
.safe_state_index = 0,
|
||||
};
|
||||
|
||||
static struct cpuidle_driver imx6ul_cpuidle_driver = {
|
||||
.name = "imx6ul_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
/* WFI */
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
/* WAIT */
|
||||
{
|
||||
.exit_latency = 50,
|
||||
.target_residency = 75,
|
||||
.enter = imx6ul_enter_wait,
|
||||
.name = "WAIT",
|
||||
.desc = "Clock off",
|
||||
},
|
||||
/* LOW POWER IDLE */
|
||||
{
|
||||
/*
|
||||
* RBC 130us + ARM gating 1370us + RBC clear 65us
|
||||
* + PLL2 relock 450us and some margin, here set
|
||||
* it to 2100us.
|
||||
*/
|
||||
.exit_latency = 2100,
|
||||
.target_residency = 2500,
|
||||
.enter = imx6ul_enter_wait,
|
||||
.name = "LOW-POWER-IDLE",
|
||||
.desc = "ARM power off",
|
||||
}
|
||||
},
|
||||
.state_count = 3,
|
||||
.safe_state_index = 0,
|
||||
};
|
||||
|
||||
int __init imx6ul_cpuidle_init(void)
|
||||
{
|
||||
void __iomem *anatop_base = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
|
||||
u32 val;
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
struct imx6_cpuidle_pm_info *cpuidle_pm_info;
|
||||
int i;
|
||||
const u32 *mmdc_offset_array;
|
||||
u32 wfi_code_size;
|
||||
|
||||
wfi_iram_base_phys = (void *)(iram_tlb_phys_addr + MX6_CPUIDLE_IRAM_ADDR_OFFSET);
|
||||
|
||||
/* Make sure wfi_iram_base is 8 byte aligned. */
|
||||
if ((uintptr_t)(wfi_iram_base_phys) & (FNCPY_ALIGN - 1))
|
||||
wfi_iram_base_phys += FNCPY_ALIGN - ((uintptr_t)wfi_iram_base_phys % (FNCPY_ALIGN));
|
||||
|
||||
wfi_iram_base = (void *)IMX_IO_P2V((unsigned long) wfi_iram_base_phys);
|
||||
|
||||
cpuidle_pm_info = wfi_iram_base;
|
||||
cpuidle_pm_info->pbase = (phys_addr_t) wfi_iram_base_phys;
|
||||
cpuidle_pm_info->pm_info_size = sizeof(*cpuidle_pm_info);
|
||||
cpuidle_pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
|
||||
cpuidle_pm_info->mmdc_io_num = ARRAY_SIZE(imx6ul_mmdc_io_offset);
|
||||
mmdc_offset_array = imx6ul_mmdc_io_offset;
|
||||
|
||||
cpuidle_pm_info->mmdc_base.pbase = MX6Q_MMDC_P0_BASE_ADDR;
|
||||
cpuidle_pm_info->mmdc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->ccm_base.pbase = MX6Q_CCM_BASE_ADDR;
|
||||
cpuidle_pm_info->ccm_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_CCM_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->anatop_base.pbase = MX6Q_ANATOP_BASE_ADDR;
|
||||
cpuidle_pm_info->anatop_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->gpc_base.pbase = MX6Q_GPC_BASE_ADDR;
|
||||
cpuidle_pm_info->gpc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_GPC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->iomuxc_base.pbase = MX6Q_IOMUXC_BASE_ADDR;
|
||||
cpuidle_pm_info->iomuxc_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->src_base.pbase = MX6Q_SRC_BASE_ADDR;
|
||||
cpuidle_pm_info->src_base.vbase = (void __iomem *)IMX_IO_P2V(MX6Q_SRC_BASE_ADDR);
|
||||
|
||||
/* Only save mmdc io offset, settings will be saved in asm code */
|
||||
for (i = 0; i < cpuidle_pm_info->mmdc_io_num; i++)
|
||||
cpuidle_pm_info->mmdc_io_val[i][0] = mmdc_offset_array[i];
|
||||
|
||||
/* calculate the wfi code size */
|
||||
if (cpu_is_imx6ul()) {
|
||||
wfi_code_size = (&mx6ul_lpm_wfi_end -&mx6ul_lpm_wfi_start) *4;
|
||||
|
||||
imx6ul_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base + sizeof(*cpuidle_pm_info),
|
||||
&imx6ul_low_power_idle, wfi_code_size);
|
||||
} else {
|
||||
wfi_code_size = (&mx6ull_lpm_wfi_end -&mx6ull_lpm_wfi_start) *4;
|
||||
|
||||
imx6ul_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base + sizeof(*cpuidle_pm_info),
|
||||
&imx6ull_low_power_idle, wfi_code_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
imx6_set_int_mem_clk_lpm(true);
|
||||
|
||||
/*
|
||||
* enable RC-OSC here, as it needs at least 4ms for RC-OSC to
|
||||
* be stable, low power idle flow can NOT endure this big
|
||||
* latency, so we make RC-OSC self-tuning enabled here.
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + PMU_LOW_PWR_CTRL);
|
||||
val |= 0x1;
|
||||
writel_relaxed(val, anatop_base + PMU_LOW_PWR_CTRL);
|
||||
/*
|
||||
* config RC-OSC freq
|
||||
* tune_enable = 1;tune_start = 1;hyst_plus = 0;hyst_minus = 0;
|
||||
* osc_prog = 0xa7;
|
||||
*/
|
||||
writel_relaxed(
|
||||
0x4 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT |
|
||||
0xa7 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_START_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set count_trg = 0x2dc */
|
||||
writel_relaxed(
|
||||
0x40 << XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT |
|
||||
0x2dc << XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
/* wait 4ms according to hardware design */
|
||||
msleep(4);
|
||||
/*
|
||||
* now add some hysteresis, hyst_plus=3, hyst_minus=3
|
||||
* (the minimum hysteresis that looks good is 2)
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
val &= ~((XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT));
|
||||
val |= (0x3 << XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(0x3 << XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT);
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set the count_1m_trg = 0x2d7 */
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
val &= ~(XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT);
|
||||
val |= 0x2d7 << XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT;
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
/*
|
||||
* hardware design require to write XTALOSC24M_OSC_CONFIG0 or
|
||||
* XTALOSC24M_OSC_CONFIG1 to
|
||||
* make XTALOSC24M_OSC_CONFIG2 write work
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
|
||||
/* ARM power up time is reduced since TO1.1 */
|
||||
if (imx_get_soc_revision() > IMX_CHIP_REVISION_1_0)
|
||||
return cpuidle_register(&imx6ul_cpuidle_driver_v2, NULL);
|
||||
else
|
||||
return cpuidle_register(&imx6ul_cpuidle_driver, NULL);
|
||||
}
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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/busfreq-imx.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/psci.h>
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/tlb.h>
|
||||
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define XTALOSC24M_OSC_CONFIG0 0x10
|
||||
#define XTALOSC24M_OSC_CONFIG1 0x20
|
||||
#define XTALOSC24M_OSC_CONFIG2 0x30
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT 24
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT 16
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK 0xf
|
||||
#define XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT 12
|
||||
#define XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT 4
|
||||
#define XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT 1
|
||||
#define XTALOSC24M_OSC_CONFIG0_START_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT 20
|
||||
#define XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT 0
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK 0xfff
|
||||
#define XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT 0
|
||||
|
||||
#define XTALOSC_CTRL_24M 0x0
|
||||
#define XTALOSC_CTRL_24M_RC_OSC_EN_SHIFT 13
|
||||
#define REG_SET 0x4
|
||||
|
||||
static void __iomem *wfi_iram_base;
|
||||
static void __iomem *wfi_iram_base_phys;
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
|
||||
struct imx7_pm_base {
|
||||
phys_addr_t pbase;
|
||||
void __iomem *vbase;
|
||||
};
|
||||
|
||||
struct imx7_cpuidle_pm_info {
|
||||
phys_addr_t vbase; /* The virtual address of pm_info. */
|
||||
phys_addr_t pbase; /* The physical address of pm_info. */
|
||||
phys_addr_t resume_addr; /* The physical resume address for asm code */
|
||||
u32 pm_info_size;
|
||||
u32 ttbr;
|
||||
u32 num_online_cpus;
|
||||
u32 num_lpi_cpus;
|
||||
atomic_t val;
|
||||
atomic_t flag0;
|
||||
atomic_t flag1;
|
||||
struct imx7_pm_base ddrc_base;
|
||||
struct imx7_pm_base ccm_base;
|
||||
struct imx7_pm_base anatop_base;
|
||||
struct imx7_pm_base src_base;
|
||||
struct imx7_pm_base iomuxc_gpr_base;
|
||||
struct imx7_pm_base gpc_base;
|
||||
struct imx7_pm_base gic_dist_base;
|
||||
} __aligned(8);
|
||||
|
||||
static atomic_t master_lpi = ATOMIC_INIT(0);
|
||||
static atomic_t master_wait = ATOMIC_INIT(0);
|
||||
|
||||
static void (*imx7d_wfi_in_iram_fn)(void __iomem *iram_vbase);
|
||||
static struct imx7_cpuidle_pm_info *cpuidle_pm_info;
|
||||
|
||||
#define MX7D_POWERDWN_IDLE_PARAM \
|
||||
((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
#define MX7D_STANDBY_IDLE_PARAM \
|
||||
((1 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_STANDBY << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
/* Mapped for the kernel, unlike cpuidle_pm_info->gic_dist_base.vbase */
|
||||
static void __iomem *imx7d_cpuidle_gic_base;
|
||||
|
||||
static void imx_pen_lock(int cpu)
|
||||
{
|
||||
if (cpu == 0) {
|
||||
atomic_set(&cpuidle_pm_info->flag0, 1);
|
||||
dsb();
|
||||
atomic_set(&cpuidle_pm_info->val, cpu);
|
||||
do {
|
||||
dsb();
|
||||
} while (atomic_read(&cpuidle_pm_info->flag1) == 1
|
||||
&& atomic_read(&cpuidle_pm_info->val) == cpu)
|
||||
;
|
||||
} else {
|
||||
atomic_set(&cpuidle_pm_info->flag1, 1);
|
||||
dsb();
|
||||
atomic_set(&cpuidle_pm_info->val, cpu);
|
||||
do {
|
||||
dsb();
|
||||
} while (atomic_read(&cpuidle_pm_info->flag0) == 1
|
||||
&& atomic_read(&cpuidle_pm_info->val) == cpu)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_pen_unlock(int cpu)
|
||||
{
|
||||
dsb();
|
||||
if (cpu == 0)
|
||||
atomic_set(&cpuidle_pm_info->flag0, 0);
|
||||
else
|
||||
atomic_set(&cpuidle_pm_info->flag1, 0);
|
||||
}
|
||||
|
||||
static int imx7d_idle_finish(unsigned long val)
|
||||
{
|
||||
if (psci_ops.cpu_suspend)
|
||||
psci_ops.cpu_suspend(MX7D_POWERDWN_IDLE_PARAM, __pa(cpu_resume));
|
||||
else
|
||||
imx7d_wfi_in_iram_fn(wfi_iram_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool imx7d_gic_sgis_pending(void)
|
||||
{
|
||||
void __iomem *sgip_base = imx7d_cpuidle_gic_base + 0x1f20;
|
||||
|
||||
return (readl_relaxed(sgip_base + 0x0) |
|
||||
readl_relaxed(sgip_base + 0x4) |
|
||||
readl_relaxed(sgip_base + 0x8) |
|
||||
readl_relaxed(sgip_base + 0xc));
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(psci_lock);
|
||||
static int imx7d_enter_low_power_idle(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
int mode = get_bus_freq_mode();
|
||||
|
||||
|
||||
if ((index == 1) || ((mode != BUS_FREQ_LOW) && index == 2)) {
|
||||
index = 1;
|
||||
if (atomic_inc_return(&master_wait) == num_online_cpus())
|
||||
imx_gpcv2_set_lpm_mode(WAIT_UNCLOCKED);
|
||||
|
||||
cpu_do_idle();
|
||||
|
||||
atomic_dec(&master_wait);
|
||||
imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
|
||||
} else {
|
||||
if (psci_ops.cpu_suspend) {
|
||||
cpu_pm_enter();
|
||||
spin_lock(&psci_lock);
|
||||
if (atomic_inc_return(&master_lpi) == num_online_cpus()) {
|
||||
if (imx7d_gic_sgis_pending()) {
|
||||
atomic_dec(&master_lpi);
|
||||
index = -1;
|
||||
goto psci_skip_lpi_flow;
|
||||
}
|
||||
|
||||
imx_gpcv2_set_lpm_mode(WAIT_UNCLOCKED);
|
||||
imx_gpcv2_set_cpu_power_gate_in_idle(true);
|
||||
|
||||
cpu_cluster_pm_enter();
|
||||
}
|
||||
spin_unlock(&psci_lock);
|
||||
|
||||
cpu_suspend(0, imx7d_idle_finish);
|
||||
|
||||
spin_lock(&psci_lock);
|
||||
if (atomic_read(&master_lpi) == num_online_cpus()) {
|
||||
cpu_cluster_pm_exit();
|
||||
imx_gpcv2_set_cpu_power_gate_in_idle(false);
|
||||
imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
|
||||
}
|
||||
|
||||
atomic_dec(&master_lpi);
|
||||
psci_skip_lpi_flow:
|
||||
spin_unlock(&psci_lock);
|
||||
cpu_pm_exit();
|
||||
} else {
|
||||
imx_pen_lock(dev->cpu);
|
||||
cpuidle_pm_info->num_online_cpus = num_online_cpus();
|
||||
++cpuidle_pm_info->num_lpi_cpus;
|
||||
cpu_pm_enter();
|
||||
if (cpuidle_pm_info->num_lpi_cpus ==
|
||||
cpuidle_pm_info->num_online_cpus) {
|
||||
/*
|
||||
* GPC will not wake on SGIs so check for them
|
||||
* manually here. At this point we know the other cpu
|
||||
* is in wfi or waiting for the lock and can't send
|
||||
* any additional IPIs.
|
||||
*/
|
||||
if (imx7d_gic_sgis_pending()) {
|
||||
index = -1;
|
||||
goto skip_lpi_flow;
|
||||
}
|
||||
imx_gpcv2_set_lpm_mode(WAIT_UNCLOCKED);
|
||||
imx_gpcv2_set_cpu_power_gate_in_idle(true);
|
||||
cpu_cluster_pm_enter();
|
||||
} else {
|
||||
imx_set_cpu_jump(dev->cpu, ca7_cpu_resume);
|
||||
}
|
||||
|
||||
cpu_suspend(0, imx7d_idle_finish);
|
||||
|
||||
if (cpuidle_pm_info->num_lpi_cpus ==
|
||||
cpuidle_pm_info->num_online_cpus) {
|
||||
cpu_cluster_pm_exit();
|
||||
imx_gpcv2_set_cpu_power_gate_in_idle(false);
|
||||
imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
|
||||
}
|
||||
|
||||
skip_lpi_flow:
|
||||
cpu_pm_exit();
|
||||
--cpuidle_pm_info->num_lpi_cpus;
|
||||
imx_pen_unlock(dev->cpu);
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static struct cpuidle_driver imx7d_cpuidle_driver = {
|
||||
.name = "imx7d_cpuidle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
/* WFI */
|
||||
ARM_CPUIDLE_WFI_STATE,
|
||||
/* WAIT MODE */
|
||||
{
|
||||
.exit_latency = 50,
|
||||
.target_residency = 75,
|
||||
.flags = CPUIDLE_FLAG_TIMER_STOP,
|
||||
.enter = imx7d_enter_low_power_idle,
|
||||
.name = "WAIT",
|
||||
.desc = "Clock off",
|
||||
},
|
||||
/* LOW POWER IDLE */
|
||||
{
|
||||
.exit_latency = 10000,
|
||||
.target_residency = 20000,
|
||||
.flags = CPUIDLE_FLAG_TIMER_STOP,
|
||||
.enter = imx7d_enter_low_power_idle,
|
||||
.name = "LOW-POWER-IDLE",
|
||||
.desc = "ARM power off",
|
||||
},
|
||||
},
|
||||
.state_count = 3,
|
||||
.safe_state_index = 0,
|
||||
};
|
||||
|
||||
int imx7d_enable_rcosc(void)
|
||||
{
|
||||
void __iomem *anatop_base =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_ANATOP_BASE_ADDR);
|
||||
u32 val;
|
||||
|
||||
imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
|
||||
/* set RC-OSC freq and turn it on */
|
||||
writel_relaxed(0x1 << XTALOSC_CTRL_24M_RC_OSC_EN_SHIFT,
|
||||
anatop_base + XTALOSC_CTRL_24M + REG_SET);
|
||||
/*
|
||||
* config RC-OSC freq
|
||||
* tune_enable = 1;tune_start = 1;hyst_plus = 0;hyst_minus = 0;
|
||||
* osc_prog = 0xa7;
|
||||
*/
|
||||
writel_relaxed(
|
||||
0x4 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_CUR_SHIFT |
|
||||
0xa7 << XTALOSC24M_OSC_CONFIG0_RC_OSC_PROG_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_ENABLE_SHIFT |
|
||||
0x1 << XTALOSC24M_OSC_CONFIG0_START_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set count_trg = 0x2dc */
|
||||
writel_relaxed(
|
||||
0x40 << XTALOSC24M_OSC_CONFIG1_COUNT_RC_CUR_SHIFT |
|
||||
0x2dc << XTALOSC24M_OSC_CONFIG1_COUNT_RC_TRG_SHIFT,
|
||||
anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
/* wait at least 4ms according to hardware design */
|
||||
mdelay(6);
|
||||
/*
|
||||
* now add some hysteresis, hyst_plus=3, hyst_minus=3
|
||||
* (the minimum hysteresis that looks good is 2)
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
val &= ~((XTALOSC24M_OSC_CONFIG0_HYST_MINUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(XTALOSC24M_OSC_CONFIG0_HYST_PLUS_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT));
|
||||
val |= (0x3 << XTALOSC24M_OSC_CONFIG0_HYST_MINUS_SHIFT) |
|
||||
(0x3 << XTALOSC24M_OSC_CONFIG0_HYST_PLUS_SHIFT);
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG0);
|
||||
/* set the count_1m_trg = 0x2d7 */
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
val &= ~(XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_MASK <<
|
||||
XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT);
|
||||
val |= 0x2d7 << XTALOSC24M_OSC_CONFIG2_COUNT_1M_TRG_SHIFT;
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG2);
|
||||
/*
|
||||
* hardware design require to write XTALOSC24M_OSC_CONFIG0 or
|
||||
* XTALOSC24M_OSC_CONFIG1 to
|
||||
* make XTALOSC24M_OSC_CONFIG2 write work
|
||||
*/
|
||||
val = readl_relaxed(anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
writel_relaxed(val, anatop_base + XTALOSC24M_OSC_CONFIG1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init imx7d_cpuidle_init(void)
|
||||
{
|
||||
wfi_iram_base_phys = (void *)(iram_tlb_phys_addr +
|
||||
MX7_CPUIDLE_OCRAM_ADDR_OFFSET);
|
||||
|
||||
/* Make sure wfi_iram_base is 8 byte aligned. */
|
||||
if ((uintptr_t)(wfi_iram_base_phys) & (FNCPY_ALIGN - 1))
|
||||
wfi_iram_base_phys += FNCPY_ALIGN -
|
||||
((uintptr_t)wfi_iram_base_phys % (FNCPY_ALIGN));
|
||||
|
||||
wfi_iram_base = (void *)IMX_IO_P2V((unsigned long) wfi_iram_base_phys);
|
||||
|
||||
cpuidle_pm_info = wfi_iram_base;
|
||||
cpuidle_pm_info->vbase = (phys_addr_t) wfi_iram_base;
|
||||
cpuidle_pm_info->pbase = (phys_addr_t) wfi_iram_base_phys;
|
||||
cpuidle_pm_info->pm_info_size = sizeof(*cpuidle_pm_info);
|
||||
cpuidle_pm_info->resume_addr = virt_to_phys(ca7_cpu_resume);
|
||||
cpuidle_pm_info->num_online_cpus = num_online_cpus();
|
||||
|
||||
cpuidle_pm_info->ddrc_base.pbase = MX7D_DDRC_BASE_ADDR;
|
||||
cpuidle_pm_info->ddrc_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_DDRC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->ccm_base.pbase = MX7D_CCM_BASE_ADDR;
|
||||
cpuidle_pm_info->ccm_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_CCM_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->anatop_base.pbase = MX7D_ANATOP_BASE_ADDR;
|
||||
cpuidle_pm_info->anatop_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_ANATOP_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->src_base.pbase = MX7D_SRC_BASE_ADDR;
|
||||
cpuidle_pm_info->src_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_SRC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->iomuxc_gpr_base.pbase = MX7D_IOMUXC_GPR_BASE_ADDR;
|
||||
cpuidle_pm_info->iomuxc_gpr_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_IOMUXC_GPR_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->gpc_base.pbase = MX7D_GPC_BASE_ADDR;
|
||||
cpuidle_pm_info->gpc_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_GPC_BASE_ADDR);
|
||||
|
||||
cpuidle_pm_info->gic_dist_base.pbase = MX7D_GIC_BASE_ADDR;
|
||||
cpuidle_pm_info->gic_dist_base.vbase =
|
||||
(void __iomem *)IMX_IO_P2V(MX7D_GIC_BASE_ADDR);
|
||||
|
||||
imx7d_cpuidle_gic_base = ioremap(MX7D_GIC_BASE_ADDR, MX7D_GIC_SIZE);
|
||||
|
||||
imx7d_enable_rcosc();
|
||||
|
||||
/* code size should include cpuidle_pm_info size */
|
||||
if (!psci_ops.cpu_suspend) {
|
||||
imx7d_wfi_in_iram_fn = (void *)fncpy(wfi_iram_base +
|
||||
sizeof(*cpuidle_pm_info),
|
||||
&imx7d_low_power_idle,
|
||||
MX7_CPUIDLE_OCRAM_SIZE - sizeof(*cpuidle_pm_info));
|
||||
}
|
||||
|
||||
return cpuidle_register(&imx7d_cpuidle_driver, NULL);
|
||||
}
|
|
@ -8,7 +8,11 @@
|
|||
extern int imx5_cpuidle_init(void);
|
||||
extern int imx6q_cpuidle_init(void);
|
||||
extern int imx6sl_cpuidle_init(void);
|
||||
extern int imx6sll_cpuidle_init(void);
|
||||
extern int imx6sx_cpuidle_init(void);
|
||||
extern int imx6ul_cpuidle_init(void);
|
||||
extern int imx7d_cpuidle_init(void);
|
||||
extern int imx7d_enable_rcosc(void);
|
||||
extern int imx7ulp_cpuidle_init(void);
|
||||
#else
|
||||
static inline int imx5_cpuidle_init(void)
|
||||
|
@ -23,10 +27,26 @@ static inline int imx6sl_cpuidle_init(void)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx6sll_cpuidle_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx6sx_cpuidle_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx6ul_cpuidle_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx7d_cpuidle_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx7d_enable_rcosc(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int imx7ulp_cpuidle_init(void)
|
||||
{
|
||||
return 0;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,764 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2015 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"
|
||||
|
||||
.globl imx6_up_ddr3_freq_change_start
|
||||
.globl imx6_up_ddr3_freq_change_end
|
||||
|
||||
#define MMDC0_MDPDC 0x4
|
||||
#define MMDC0_MDCF0 0xc
|
||||
#define MMDC0_MDCF1 0x10
|
||||
#define MMDC0_MDMISC 0x18
|
||||
#define MMDC0_MDSCR 0x1c
|
||||
#define MMDC0_MAPSR 0x404
|
||||
#define MMDC0_MADPCR0 0x410
|
||||
#define MMDC0_MPZQHWCTRL 0x800
|
||||
#define MMDC0_MPODTCTRL 0x818
|
||||
#define MMDC0_MPDGCTRL0 0x83c
|
||||
#define MMDC0_MPMUR0 0x8b8
|
||||
|
||||
#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 BUSFREQ_INFO_FREQ_OFFSET 0x0
|
||||
#define BUSFREQ_INFO_DDR_SETTINGS_OFFSET 0x4
|
||||
#define BUSFREQ_INFO_DLL_OFF_OFFSET 0x8
|
||||
#define BUSFREQ_INFO_IOMUX_OFFSETS_OFFSET 0xc
|
||||
#define BUSFREQ_INFO_MU_DELAY_OFFSET 0x10
|
||||
|
||||
.extern iram_tlb_phys_addr
|
||||
|
||||
.align 3
|
||||
|
||||
/* Check if the cpu is cortex-a7 */
|
||||
.macro is_ca7
|
||||
|
||||
/* Read the primary cpu number is MPIDR */
|
||||
mrc p15, 0, r7, c0, c0, 0
|
||||
ldr r8, =0xfff0
|
||||
and r7, r7, r8
|
||||
ldr r8, =0xc070
|
||||
cmp r7, r8
|
||||
|
||||
.endm
|
||||
|
||||
.macro do_delay
|
||||
|
||||
1:
|
||||
ldr r9, =0
|
||||
2:
|
||||
ldr r10, [r4, r9]
|
||||
add r9, r9, #4
|
||||
cmp r9, #16
|
||||
bne 2b
|
||||
sub r8, r8, #1
|
||||
cmp r8, #0
|
||||
bgt 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro wait_for_ccm_handshake
|
||||
|
||||
3:
|
||||
ldr r8, [r5, #CCM_CDHIPR]
|
||||
cmp r8, #0
|
||||
bne 3b
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_400MHz
|
||||
|
||||
/* check whether periph2_clk is already from top path */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
ands r8, #(1 << 26)
|
||||
beq skip_periph2_clk2_switch_400m
|
||||
|
||||
/* now switch periph2_clk back. */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
bic r8, r8, #(1 << 26)
|
||||
str r8, [r5, #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, [r5, #CCM_CBCDR]
|
||||
bic r8, r8, #(0x7 << 3)
|
||||
str r8, [r5, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_50MHz
|
||||
|
||||
/* check whether periph2_clk is already from top path */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
ands r8, #(1 << 26)
|
||||
beq skip_periph2_clk2_switch_50m
|
||||
|
||||
/* now switch periph2_clk back. */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
bic r8, r8, #(1 << 26)
|
||||
str r8, [r5, #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_50m:
|
||||
|
||||
/* fabric_mmdc_podf to 7 so that mmdc is 400 / 8 = 50MHz */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
orr r8, r8, #(0x7 << 3)
|
||||
str r8, [r5, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_24MHz
|
||||
|
||||
/* periph2_clk2 sel to OSC_CLK */
|
||||
ldr r8, [r5, #CCM_CBCMR]
|
||||
orr r8, r8, #(1 << 20)
|
||||
str r8, [r5, #CCM_CBCMR]
|
||||
|
||||
/* periph2_clk2_podf to 0 */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
bic r8, r8, #0x7
|
||||
str r8, [r5, #CCM_CBCDR]
|
||||
|
||||
/* periph2_clk sel to periph2_clk2 */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
orr r8, r8, #(0x1 << 26)
|
||||
str r8, [r5, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
/* fabric_mmdc_podf to 0 */
|
||||
ldr r8, [r5, #CCM_CBCDR]
|
||||
bic r8, r8, #(0x7 << 3)
|
||||
str r8, [r5, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
.endm
|
||||
|
||||
/*
|
||||
* imx6_up_ddr3_freq_change
|
||||
* Below code can be used by i.MX6SX and i.MX6UL.
|
||||
*
|
||||
* idle the processor (eg, wait for interrupt).
|
||||
* make sure DDR is in self-refresh.
|
||||
* IRQs are already disabled.
|
||||
*/
|
||||
ENTRY(imx6_up_ddr3_freq_change)
|
||||
|
||||
imx6_up_ddr3_freq_change_start:
|
||||
stmfd sp!, {r4 - r11}
|
||||
|
||||
ldr r1, [r0, #BUSFREQ_INFO_DDR_SETTINGS_OFFSET]
|
||||
ldr r2, [r0, #BUSFREQ_INFO_DLL_OFF_OFFSET]
|
||||
ldr r3, [r0, #BUSFREQ_INFO_IOMUX_OFFSETS_OFFSET]
|
||||
|
||||
/*
|
||||
* 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]
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the Branch Target Address Cache (BTAC) */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
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
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Disable L1 data cache. */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
bic r6, r6, #0x4
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
|
||||
ldr r4, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
|
||||
ldr r5, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
|
||||
ldr r6, =IMX_IO_P2V(MX6Q_IOMUXC_BASE_ADDR)
|
||||
|
||||
is_ca7
|
||||
beq skip_disable_l2
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/*
|
||||
* make sure the L2 buffers are drained,
|
||||
* sync operation on L2 drains the buffers.
|
||||
*/
|
||||
ldr r8, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
|
||||
|
||||
/* Wait for background operations to complete. */
|
||||
wait_for_l2_to_idle:
|
||||
ldr r7, [r8, #0x730]
|
||||
cmp r7, #0x0
|
||||
bne wait_for_l2_to_idle
|
||||
|
||||
mov r7, #0x0
|
||||
str r7, [r8, #L2_CACHE_SYNC]
|
||||
|
||||
/* Lock L2. */
|
||||
|
||||
ldr r9, [r8, #PL310_AUX_CTRL]
|
||||
tst r9, #PL310_AUX_16WAY_BIT
|
||||
mov r9, #PL310_8WAYS_MASK
|
||||
orrne r9, #PL310_16WAYS_UPPERMASK
|
||||
mov r10, #PL310_LOCKDOWN_NBREGS
|
||||
add r11, r8, #PL310_DCACHE_LOCKDOWN_BASE
|
||||
1: /* lock Dcache and Icache */
|
||||
str r9, [r11], #PL310_LOCKDOWN_SZREG
|
||||
str r9, [r11], #PL310_LOCKDOWN_SZREG
|
||||
subs r10, r10, #1
|
||||
bne 1b
|
||||
|
||||
/*
|
||||
* The second dsb might be needed to keep cache sync (device write)
|
||||
* ordering with the memory accesses before it.
|
||||
*/
|
||||
dsb
|
||||
isb
|
||||
#endif
|
||||
|
||||
skip_disable_l2:
|
||||
/* disable automatic power saving. */
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
orr r8, r8, #0x1
|
||||
str r8, [r4, #MMDC0_MAPSR]
|
||||
|
||||
/* disable MMDC power down timer. */
|
||||
ldr r8, [r4, #MMDC0_MDPDC]
|
||||
bic r8, r8, #(0xff << 8)
|
||||
str r8, [r4, #MMDC0_MDPDC]
|
||||
|
||||
/* delay for a while */
|
||||
ldr r8, =4
|
||||
do_delay
|
||||
|
||||
/* set CON_REG */
|
||||
ldr r8, =0x8000
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
poll_conreq_set_1:
|
||||
ldr r8, [r4, #MMDC0_MDSCR]
|
||||
and r8, r8, #(0x4 << 12)
|
||||
cmp r8, #(0x4 << 12)
|
||||
bne poll_conreq_set_1
|
||||
|
||||
/*
|
||||
* if requested frequency is greater than
|
||||
* 300MHz go to DLL on mode.
|
||||
*/
|
||||
ldr r8, [r0, #BUSFREQ_INFO_FREQ_OFFSET]
|
||||
ldr r9, =300000000
|
||||
cmp r8, r9
|
||||
bge dll_on_mode
|
||||
|
||||
dll_off_mode:
|
||||
/* if DLL is currently on, turn it off. */
|
||||
cmp r2, #1
|
||||
beq continue_dll_off_1
|
||||
|
||||
ldr r8, =0x00018031
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x00018039
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =10
|
||||
do_delay
|
||||
|
||||
continue_dll_off_1:
|
||||
/* set DVFS - enter self refresh mode */
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
orr r8, r8, #(1 << 21)
|
||||
str r8, [r4, #MMDC0_MAPSR]
|
||||
|
||||
/* de-assert con_req */
|
||||
mov r8, #0x0
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
poll_dvfs_set_1:
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
and r8, r8, #(1 << 25)
|
||||
cmp r8, #(1 << 25)
|
||||
bne poll_dvfs_set_1
|
||||
|
||||
ldr r8, [r0, #BUSFREQ_INFO_FREQ_OFFSET]
|
||||
ldr r9, =24000000
|
||||
cmp r8, r9
|
||||
beq switch_freq_24
|
||||
|
||||
switch_to_50MHz
|
||||
b continue_dll_off_2
|
||||
|
||||
switch_freq_24:
|
||||
switch_to_24MHz
|
||||
|
||||
continue_dll_off_2:
|
||||
/* set SBS - block ddr accesses */
|
||||
ldr r8, [r4, #MMDC0_MADPCR0]
|
||||
orr r8, r8, #(1 << 8)
|
||||
str r8, [r4, #MMDC0_MADPCR0]
|
||||
|
||||
/* clear DVFS - exit from self refresh mode */
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
bic r8, r8, #(1 << 21)
|
||||
str r8, [r4, #MMDC0_MAPSR]
|
||||
|
||||
poll_dvfs_clear_1:
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
and r8, r8, #(1 << 25)
|
||||
cmp r8, #(1 << 25)
|
||||
beq poll_dvfs_clear_1
|
||||
|
||||
/* if DLL was previously on, continue DLL off routine. */
|
||||
cmp r2, #1
|
||||
beq continue_dll_off_3
|
||||
|
||||
ldr r8, =0x00018031
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x00018039
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x04208030
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x04208038
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x00088032
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x0008803A
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
/* delay for a while. */
|
||||
ldr r8, =4
|
||||
do_delay
|
||||
|
||||
ldr r8, [r4, #MMDC0_MDCF0]
|
||||
bic r8, r8, #0xf
|
||||
orr r8, r8, #0x3
|
||||
str r8, [r4, #MMDC0_MDCF0]
|
||||
|
||||
ldr r8, [r4, #MMDC0_MDCF1]
|
||||
bic r8, r8, #0x7
|
||||
orr r8, r8, #0x4
|
||||
str r8, [r4, #MMDC0_MDCF1]
|
||||
|
||||
ldr r8, [r4, #MMDC0_MDMISC]
|
||||
bic r8, r8, #(0x3 << 16) /* walat = 0x1 */
|
||||
orr r8, r8, #(0x1 << 16)
|
||||
bic r8, r8, #(0x7 << 6) /* ralat = 0x2 */
|
||||
orr r8, r8, #(0x2 << 6)
|
||||
str r8, [r4, #MMDC0_MDMISC]
|
||||
|
||||
/* enable dqs pull down in the IOMUX. */
|
||||
ldr r8, [r3]
|
||||
add r3, r3, #8
|
||||
ldr r9, =0x3028
|
||||
update_iomux:
|
||||
ldr r10, [r3]
|
||||
ldr r11, [r6, r10]
|
||||
bic r11, r11, r9
|
||||
orr r11, r11, #(0x3 << 12)
|
||||
orr r11, r11, #0x28
|
||||
str r11, [r6, r10]
|
||||
add r3, r3, #8
|
||||
sub r8, r8, #1
|
||||
cmp r8, #0
|
||||
bgt update_iomux
|
||||
|
||||
/* ODT disabled. */
|
||||
ldr r8, =0x0
|
||||
str r8, [r4, #MMDC0_MPODTCTRL]
|
||||
|
||||
/* DQS gating disabled. */
|
||||
ldr r8, [r4, #MMDC0_MPDGCTRL0]
|
||||
orr r8, r8, #(1 << 29)
|
||||
str r8, [r4, #MMDC0_MPDGCTRL0]
|
||||
|
||||
/* Add workaround for ERR005778.*/
|
||||
/* double the original MU_UNIT_DEL_NUM. */
|
||||
ldr r8, [r0, #BUSFREQ_INFO_MU_DELAY_OFFSET]
|
||||
lsl r8, r8, #1
|
||||
|
||||
/* Bypass the automatic MU by setting the mu_byp_en */
|
||||
ldr r10, [r4, #MMDC0_MPMUR0]
|
||||
orr r10, r10, #0x400
|
||||
/* Set the MU_BYP_VAL */
|
||||
orr r10, r10, r8
|
||||
str r10, [r4, #MMDC0_MPMUR0]
|
||||
|
||||
/* Now perform a force measure */
|
||||
ldr r8, [r4, #MMDC0_MPMUR0]
|
||||
orr r8, r8, #0x800
|
||||
str r8, [r4, #MMDC0_MPMUR0]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
1:
|
||||
ldr r8, [r4, #MMDC0_MPMUR0]
|
||||
and r8, r8, #0x800
|
||||
cmp r8, #0x0
|
||||
bne 1b
|
||||
|
||||
continue_dll_off_3:
|
||||
/* clear SBS - unblock accesses to DDR. */
|
||||
ldr r8, [r4, #MMDC0_MADPCR0]
|
||||
bic r8, r8, #(0x1 << 8)
|
||||
str r8, [r4, #MMDC0_MADPCR0]
|
||||
|
||||
mov r8, #0x0
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
poll_conreq_clear_1:
|
||||
ldr r8, [r4, #MMDC0_MDSCR]
|
||||
and r8, r8, #(0x4 << 12)
|
||||
cmp r8, #(0x4 << 12)
|
||||
beq poll_conreq_clear_1
|
||||
|
||||
b done
|
||||
|
||||
dll_on_mode:
|
||||
/* assert DVFS - enter self refresh mode. */
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
orr r8, r8, #(1 << 21)
|
||||
str r8, [r4, #MMDC0_MAPSR]
|
||||
|
||||
/* de-assert CON_REQ. */
|
||||
mov r8, #0x0
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
/* poll DVFS ack. */
|
||||
poll_dvfs_set_2:
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
and r8, r8, #(1 << 25)
|
||||
cmp r8, #(1 << 25)
|
||||
bne poll_dvfs_set_2
|
||||
|
||||
switch_to_400MHz
|
||||
|
||||
/* set SBS step-by-step mode. */
|
||||
ldr r8, [r4, #MMDC0_MADPCR0]
|
||||
orr r8, r8, #(1 << 8)
|
||||
str r8, [r4, #MMDC0_MADPCR0]
|
||||
|
||||
/* clear DVFS - exit self refresh mode. */
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
bic r8, r8, #(1 << 21)
|
||||
str r8, [r4, #MMDC0_MAPSR]
|
||||
|
||||
poll_dvfs_clear_2:
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
ands r8, r8, #(1 << 25)
|
||||
bne poll_dvfs_clear_2
|
||||
|
||||
/* if DLL is currently off, turn it back on. */
|
||||
cmp r2, #0
|
||||
beq update_calibration_only
|
||||
|
||||
/* issue zq calibration command */
|
||||
ldr r8, [r4, #MMDC0_MPZQHWCTRL]
|
||||
orr r8, r8, #0x3
|
||||
str r8, [r4, #MMDC0_MPZQHWCTRL]
|
||||
|
||||
/* enable DQS gating. */
|
||||
ldr r10, =MMDC0_MPDGCTRL0
|
||||
ldr r8, [r4, r10]
|
||||
bic r8, r8, #(1 << 29)
|
||||
str r8, [r4, r10]
|
||||
|
||||
/* Now perform a force measure */
|
||||
ldr r8, =0x00000800
|
||||
str r8, [r4, #MMDC0_MPMUR0]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
1:
|
||||
ldr r8, [r4, #MMDC0_MPMUR0]
|
||||
and r8, r8, #0x800
|
||||
cmp r8, #0x0
|
||||
bne 1b
|
||||
|
||||
/* disable dqs pull down in the IOMUX. */
|
||||
ldr r8, [r3]
|
||||
add r3, r3, #8
|
||||
update_iomux1:
|
||||
ldr r10, [r3, #0x0]
|
||||
ldr r11, [r3, #0x4]
|
||||
str r11, [r6, r10]
|
||||
add r3, r3, #8
|
||||
sub r8, r8, #1
|
||||
cmp r8, #0
|
||||
bgt update_iomux1
|
||||
|
||||
/* config MMDC timings to 400MHz. */
|
||||
ldr r1, [r0, #BUSFREQ_INFO_DDR_SETTINGS_OFFSET]
|
||||
ldr r7, [r1]
|
||||
add r1, r1, #8
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
|
||||
/* configure ddr devices to dll on, odt. */
|
||||
ldr r8, =0x00028031
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x00028039
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
/* delay for while. */
|
||||
ldr r8, =4
|
||||
do_delay
|
||||
|
||||
/* reset dll. */
|
||||
ldr r8, =0x09208030
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x09208038
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
/* delay for while. */
|
||||
ldr r8, =100
|
||||
do_delay
|
||||
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
|
||||
ldr r8, =0x00428031
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x00428039
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
|
||||
/* issue a zq command. */
|
||||
ldr r8, =0x04008040
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
ldr r8, =0x04008048
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
|
||||
/* MMDC ODT enable. */
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
|
||||
/* delay for while. */
|
||||
ldr r8, =40
|
||||
do_delay
|
||||
|
||||
/* enable MMDC power down timer. */
|
||||
ldr r8, [r4, #MMDC0_MDPDC]
|
||||
orr r8, r8, #(0x55 << 8)
|
||||
str r8, [r4, #MMDC0_MDPDC]
|
||||
|
||||
b update_calibration
|
||||
|
||||
update_calibration_only:
|
||||
ldr r8, [r1]
|
||||
sub r8, r8, #7
|
||||
add r1, r1, #64
|
||||
b update_calib
|
||||
|
||||
update_calibration:
|
||||
/* write the new calibration values. */
|
||||
mov r8, r7
|
||||
sub r8, r8, #7
|
||||
|
||||
update_calib:
|
||||
ldr r10, [r1, #0x0]
|
||||
ldr r11, [r1, #0x4]
|
||||
str r11, [r4, r10]
|
||||
add r1, r1, #8
|
||||
sub r8, r8, #1
|
||||
cmp r8, #0
|
||||
bgt update_calib
|
||||
|
||||
/* perform a force measurement. */
|
||||
ldr r8, =0x800
|
||||
str r8, [r4, #MMDC0_MPMUR0]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
1:
|
||||
ldr r8, [r4, #MMDC0_MPMUR0]
|
||||
and r8, r8, #0x800
|
||||
cmp r8, #0x0
|
||||
bne 1b
|
||||
|
||||
/* clear SBS - unblock DDR accesses. */
|
||||
ldr r8, [r4, #MMDC0_MADPCR0]
|
||||
bic r8, r8, #(1 << 8)
|
||||
str r8, [r4, #MMDC0_MADPCR0]
|
||||
|
||||
mov r8, #0x0
|
||||
str r8, [r4, #MMDC0_MDSCR]
|
||||
poll_conreq_clear_2:
|
||||
ldr r8, [r4, #MMDC0_MDSCR]
|
||||
and r8, r8, #(0x4 << 12)
|
||||
cmp r8, #(0x4 << 12)
|
||||
beq poll_conreq_clear_2
|
||||
|
||||
done:
|
||||
|
||||
/* MMDC0_MAPSR adopt power down enable. */
|
||||
ldr r8, [r4, #MMDC0_MAPSR]
|
||||
bic r8, r8, #0x01
|
||||
str r8, [r4, #MMDC0_MAPSR]
|
||||
|
||||
is_ca7
|
||||
beq skip_enable_l2
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* Unlock L2. */
|
||||
ldr r8, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
|
||||
ldr r9, [r8, #PL310_AUX_CTRL]
|
||||
tst r9, #PL310_AUX_16WAY_BIT
|
||||
mov r10, #PL310_LOCKDOWN_NBREGS
|
||||
mov r9, #0x00 /* 8 ways mask */
|
||||
orrne r9, #0x0000 /* 16 ways mask */
|
||||
add r11, r8, #PL310_DCACHE_LOCKDOWN_BASE
|
||||
1: /* lock Dcache and Icache */
|
||||
str r9, [r11], #PL310_LOCKDOWN_SZREG
|
||||
str r9, [r11], #PL310_LOCKDOWN_SZREG
|
||||
subs r10, r10, #1
|
||||
bne 1b
|
||||
|
||||
#endif
|
||||
|
||||
skip_enable_l2:
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
orr r7, r7, #0x4
|
||||
mcr p15, 0, r7, 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, r7, c1, c0, 0
|
||||
orr r7, r7, #0x800
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
|
||||
/* Flush the Branch Target Address Cache (BTAC) */
|
||||
ldr r7, =0x0
|
||||
mcr p15, 0, r7, c7, c1, 6
|
||||
|
||||
/* restore registers */
|
||||
ldmfd sp!, {r4 - r11}
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* Add ltorg here to ensure that all
|
||||
* literals are stored here and are
|
||||
* within the text space.
|
||||
*/
|
||||
.ltorg
|
||||
imx6_up_ddr3_freq_change_end:
|
||||
ENDPROC(imx6_up_ddr3_freq_change)
|
|
@ -0,0 +1,586 @@
|
|||
/*
|
||||
* Copyright (C) 2015-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 DDRC_MSTR 0x0
|
||||
#define DDRC_STAT 0x4
|
||||
#define DDRC_MRCTRL0 0x10
|
||||
#define DDRC_MRCTRL1 0x14
|
||||
#define DDRC_MRSTAT 0x18
|
||||
#define DDRC_PWRCTL 0x30
|
||||
#define DDRC_RFSHCTL3 0x60
|
||||
#define DDRC_RFSHTMG 0x64
|
||||
#define DDRC_DBG1 0x304
|
||||
#define DDRC_SWCTL 0x320
|
||||
#define DDRC_SWSTAT 0x324
|
||||
#define DDRC_PSTAT 0x3fc
|
||||
#define DDRC_PCTRL_0 0x490
|
||||
#define DDRC_ZQCTL0 0x180
|
||||
#define DDRC_DFIMISC 0x1b0
|
||||
#define DDRC_DBGCAM 0x308
|
||||
#define DDRPHY_LP_CON0 0x18
|
||||
#define IOMUXC_GPR8 0x20
|
||||
#define DDRPHY_MDLL_CON0 0xb0
|
||||
#define DDRPHY_MDLL_CON1 0xb4
|
||||
#define DDRPHY_OFFSETD_CON0 0x50
|
||||
#define DDRPHY_OFFSETR_CON0 0x20
|
||||
#define DDRPHY_OFFSETR_CON1 0x24
|
||||
#define DDRPHY_OFFSETR_CON2 0x28
|
||||
#define DDRPHY_OFFSETW_CON0 0x30
|
||||
#define DDRPHY_OFFSETW_CON1 0x34
|
||||
#define DDRPHY_OFFSETW_CON2 0x38
|
||||
#define DDRPHY_CA_WLDSKEW_CON0 0x6c
|
||||
#define DDRPHY_CA_DSKEW_CON0 0x7c
|
||||
#define DDRPHY_CA_DSKEW_CON1 0x80
|
||||
#define DDRPHY_CA_DSKEW_CON2 0x84
|
||||
|
||||
#define ANADIG_DIGPROG 0x800
|
||||
|
||||
.align 3
|
||||
|
||||
.macro switch_to_below_100m
|
||||
|
||||
ldr r7, =0x2
|
||||
str r7, [r4, #DDRC_DBG1]
|
||||
|
||||
ldr r6, =0x36000000
|
||||
1:
|
||||
ldr r7, [r4, #DDRC_DBGCAM]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 1b
|
||||
|
||||
ldr r6, =0x1
|
||||
2:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 2b
|
||||
|
||||
ldr r7, =0x10f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800010f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r6, =0x1
|
||||
3:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 3b
|
||||
|
||||
ldr r7, =0x20f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x8
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800020f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r6, =0x1
|
||||
4:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 4b
|
||||
|
||||
ldr r7, =0x10f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x1
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800010f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r7, =0x20
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
ldr r6, =0x23
|
||||
5:
|
||||
ldr r7, [r4, #DDRC_STAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 5b
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_SWCTL]
|
||||
|
||||
ldr r7, =0x03048001
|
||||
str r7, [r4, #DDRC_MSTR]
|
||||
|
||||
ldr r7, =0x1
|
||||
str r7, [r4, #DDRC_SWCTL]
|
||||
|
||||
ldr r6, =0x1
|
||||
6:
|
||||
ldr r7, [r4, #DDRC_SWSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 6b
|
||||
|
||||
ldr r7, =0x10010100
|
||||
str r7, [r5, #0x4]
|
||||
|
||||
ldr r6, =24000000
|
||||
cmp r0, r6
|
||||
beq 25f
|
||||
|
||||
ldr r7, =0x000B000D
|
||||
str r7,[r4, #DDRC_RFSHTMG]
|
||||
b 7f
|
||||
|
||||
25:
|
||||
ldr r7, =0x00030004
|
||||
str r7,[r4, #DDRC_RFSHTMG]
|
||||
|
||||
/* dram alt sel set to OSC */
|
||||
ldr r7, =0x10000000
|
||||
ldr r8, =0xa080
|
||||
str r7, [r2, r8]
|
||||
/* dram root set to from dram alt, div by 1 */
|
||||
ldr r7, =0x11000000
|
||||
ldr r8, =0x9880
|
||||
str r7, [r2, r8]
|
||||
b 8f
|
||||
7:
|
||||
/* dram alt sel set to pfd0_392m */
|
||||
ldr r7, =0x15000000
|
||||
ldr r8, =0xa080
|
||||
str r7, [r2, r8]
|
||||
/* dram root set to from dram alt, div by 4 */
|
||||
ldr r7, =0x11000003
|
||||
ldr r8, =0x9880
|
||||
str r7, [r2, r8]
|
||||
8:
|
||||
ldr r7, =0x202ffd0
|
||||
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||
|
||||
ldr r7, =0x1000007f
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
ldr r7, =0x7f7f7f7f
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||
ldr r7, =0x7f
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||
|
||||
ldr r7, =0x7f7f7f7f
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||
ldr r7, =0x7f
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||
|
||||
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||
and r7, r7, #0x11
|
||||
cmp r7, #0x11
|
||||
bne 20f
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||
ldr r7, =0x60606060
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
ldr r7, =0x00006060
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
b 21f
|
||||
20:
|
||||
ldr r7, =0x0
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
21:
|
||||
ldr r7, =0x1100007f
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
ldr r7, =0x1000007f
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
ldr r6, =0x1
|
||||
9:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 9b
|
||||
|
||||
ldr r7, =0xf0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x820
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800000f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r6, =0x1
|
||||
10:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 10b
|
||||
|
||||
ldr r7, =0x800020
|
||||
str r7, [r4, #DDRC_ZQCTL0]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_DBG1]
|
||||
|
||||
/* enable auto self-refresh */
|
||||
ldr r7, [r4, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 0)
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_533m
|
||||
|
||||
ldr r7, =0x2
|
||||
str r7, [r4, #DDRC_DBG1]
|
||||
|
||||
ldr r7, =0x78
|
||||
str r7, [r3, #IOMUXC_GPR8]
|
||||
orr r7, r7, #0x100
|
||||
str r7, [r3, #IOMUXC_GPR8]
|
||||
|
||||
ldr r6, =0x30000000
|
||||
11:
|
||||
ldr r7, [r4, #DDRC_DBGCAM]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 11b
|
||||
|
||||
ldr r6, =0x1
|
||||
12:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 12b
|
||||
|
||||
ldr r7, =0x10f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x1
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800010f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r7, =0x20
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
ldr r6, =0x23
|
||||
13:
|
||||
ldr r7, [r4, #DDRC_STAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 13b
|
||||
|
||||
ldr r7, =0x03040001
|
||||
str r7, [r4, #DDRC_MSTR]
|
||||
|
||||
ldr r7, =0x40800020
|
||||
str r7, [r4, #DDRC_ZQCTL0]
|
||||
|
||||
|
||||
ldr r7, =0x10210100
|
||||
str r7, [r5, #0x4]
|
||||
|
||||
ldr r7, =0x00040046
|
||||
str r7, [r4, #DDRC_RFSHTMG]
|
||||
|
||||
/* dram root set to from dram main, div by 2 */
|
||||
ldr r7, =0x10000001
|
||||
ldr r8, =0x9880
|
||||
str r7, [r2, r8]
|
||||
|
||||
ldr r7, =0x1010007e
|
||||
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||
|
||||
ldr r7, =0x10000008
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
ldr r7, =0x08080808
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||
ldr r7, =0x8
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||
|
||||
ldr r7, =0x08080808
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||
ldr r7, =0x8
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||
|
||||
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||
and r7, r7, #0x11
|
||||
cmp r7, #0x11
|
||||
bne 22f
|
||||
|
||||
ldr r7, =0x40404040
|
||||
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||
ldr r7, =0x18181818
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
ldr r7, =0x40401818
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
b 23f
|
||||
22:
|
||||
ldr r7, =0x0
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
23:
|
||||
ldr r7, =0x11000008
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
ldr r7, =0x10000008
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
ldr r6, =0x4
|
||||
14:
|
||||
ldr r7, [r5, #DDRPHY_MDLL_CON1]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 14b
|
||||
|
||||
ldr r7, =0x1
|
||||
str r7, [r4, #DDRC_RFSHCTL3]
|
||||
ldr r7, =0x3
|
||||
str r7, [r4, #DDRC_RFSHCTL3]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
ldr r6, =0x1
|
||||
15:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 15b
|
||||
|
||||
ldr r7, =0x10f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800010f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r6, =0x1
|
||||
16:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 16b
|
||||
|
||||
ldr r7, =0xf0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x930
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800000f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_RFSHCTL3]
|
||||
ldr r7, =0x2
|
||||
str r7, [r4, #DDRC_RFSHCTL3]
|
||||
|
||||
ldr r6, =0x1
|
||||
17:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 17b
|
||||
|
||||
ldr r7, =0xf0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x930
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800000f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r6, =0x1
|
||||
18:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 18b
|
||||
|
||||
ldr r7, =0x20f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x408
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800020f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r6, =0x1
|
||||
19:
|
||||
ldr r7, [r4, #DDRC_MRSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 19b
|
||||
|
||||
ldr r7, =0x10f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
ldr r7, =0x4
|
||||
str r7, [r4, #DDRC_MRCTRL1]
|
||||
ldr r7, =0x800010f0
|
||||
str r7, [r4, #DDRC_MRCTRL0]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_DBG1]
|
||||
|
||||
/* enable auto self-refresh */
|
||||
ldr r7, [r4, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 0)
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
.endm
|
||||
|
||||
ENTRY(imx7d_ddr3_freq_change)
|
||||
push {r2 - r9}
|
||||
|
||||
/*
|
||||
* 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, =0x0
|
||||
mcr p15, 0, r6, c8, c3, 0
|
||||
|
||||
ldr r6, =iram_tlb_phys_addr
|
||||
ldr r7, [r6]
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the Branch Target Address Cache (BTAC) */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
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
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
ldr r2, =IMX_IO_P2V(MX7D_CCM_BASE_ADDR)
|
||||
ldr r3, =IMX_IO_P2V(MX7D_IOMUXC_GPR_BASE_ADDR)
|
||||
ldr r4, =IMX_IO_P2V(MX7D_DDRC_BASE_ADDR)
|
||||
ldr r5, =IMX_IO_P2V(MX7D_DDRC_PHY_BASE_ADDR)
|
||||
ldr r9, =IMX_IO_P2V(MX7D_ANATOP_BASE_ADDR)
|
||||
|
||||
ldr r6, =100000000
|
||||
cmp r0, r6
|
||||
bgt set_to_533m
|
||||
|
||||
set_to_below_100m:
|
||||
switch_to_below_100m
|
||||
b done
|
||||
|
||||
set_to_533m:
|
||||
switch_to_533m
|
||||
b done
|
||||
|
||||
done:
|
||||
/* 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
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
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 - r9}
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
ENDPROC(imx7d_ddr3_freq_change)
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "hardware.h"
|
||||
|
||||
#define DDRC_MSTR 0x0
|
||||
#define BM_DDRC_MSTR_DDR3 0x1
|
||||
#define BM_DDRC_MSTR_LPDDR2 0x4
|
||||
#define BM_DDRC_MSTR_LPDDR3 0x8
|
||||
|
||||
static int ddr_type;
|
||||
|
||||
static int imx_ddrc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
void __iomem *ddrc_base, *reg;
|
||||
u32 val;
|
||||
|
||||
ddrc_base = of_iomap(np, 0);
|
||||
WARN_ON(!ddrc_base);
|
||||
|
||||
reg = ddrc_base + DDRC_MSTR;
|
||||
/* Get ddr type */
|
||||
val = readl_relaxed(reg);
|
||||
val &= (BM_DDRC_MSTR_DDR3 | BM_DDRC_MSTR_LPDDR2
|
||||
| BM_DDRC_MSTR_LPDDR3);
|
||||
|
||||
switch (val) {
|
||||
case BM_DDRC_MSTR_DDR3:
|
||||
pr_info("DDR type is DDR3!\n");
|
||||
ddr_type = IMX_DDR_TYPE_DDR3;
|
||||
break;
|
||||
case BM_DDRC_MSTR_LPDDR2:
|
||||
pr_info("DDR type is LPDDR2!\n");
|
||||
ddr_type = IMX_DDR_TYPE_LPDDR2;
|
||||
break;
|
||||
case BM_DDRC_MSTR_LPDDR3:
|
||||
pr_info("DDR type is LPDDR3!\n");
|
||||
ddr_type = IMX_DDR_TYPE_LPDDR3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imx_ddrc_get_ddr_type(void)
|
||||
{
|
||||
return ddr_type;
|
||||
}
|
||||
|
||||
static struct of_device_id imx_ddrc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx7-ddrc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver imx_ddrc_driver = {
|
||||
.driver = {
|
||||
.name = "imx-ddrc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = imx_ddrc_dt_ids,
|
||||
},
|
||||
.probe = imx_ddrc_probe,
|
||||
};
|
||||
|
||||
static int __init imx_ddrc_init(void)
|
||||
{
|
||||
return platform_driver_register(&imx_ddrc_driver);
|
||||
}
|
||||
postcore_initcall(imx_ddrc_init);
|
|
@ -14,22 +14,153 @@
|
|||
#include "common.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define GPC_CNTR 0x0
|
||||
#define GPC_CNTR 0x000
|
||||
#define GPC_CNTR_L2_PGE 22
|
||||
|
||||
#define GPC_IMR1 0x008
|
||||
#define GPC_PGC_MF_PDN 0x220
|
||||
#define GPC_PGC_CPU_PDN 0x2a0
|
||||
#define GPC_PGC_CPU_PUPSCR 0x2a4
|
||||
#define GPC_PGC_CPU_PDNSCR 0x2a8
|
||||
#define GPC_PGC_SW2ISO_SHIFT 0x8
|
||||
#define GPC_PGC_SW_SHIFT 0x0
|
||||
#define GPC_M4_LPSR 0x2c
|
||||
#define GPC_M4_LPSR_M4_SLEEPING_SHIFT 4
|
||||
#define GPC_M4_LPSR_M4_SLEEPING_MASK 0x1
|
||||
#define GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_MASK 0x1
|
||||
#define GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_SHIFT 0
|
||||
#define GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_MASK 0x1
|
||||
#define GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_SHIFT 1
|
||||
|
||||
#define GPC_CNTR_L2_PGE_SHIFT 22
|
||||
#define GPC_PGC_CPU_SW_SHIFT 0
|
||||
#define GPC_PGC_CPU_SW_MASK 0x3f
|
||||
#define GPC_PGC_CPU_SW2ISO_SHIFT 8
|
||||
#define GPC_PGC_CPU_SW2ISO_MASK 0x3f
|
||||
|
||||
#define IMR_NUM 4
|
||||
#define GPC_MAX_IRQS (IMR_NUM * 32)
|
||||
|
||||
/* for irq #74 and #75 */
|
||||
#define GPC_USB_VBUS_WAKEUP_IRQ_MASK 0xc00
|
||||
|
||||
/* for irq #150 and #151 */
|
||||
#define GPC_ENET_WAKEUP_IRQ_MASK 0xC00000
|
||||
|
||||
static void __iomem *gpc_base;
|
||||
static u32 gpc_wake_irqs[IMR_NUM];
|
||||
static u32 gpc_saved_imrs[IMR_NUM];
|
||||
static u32 gpc_mf_irqs[IMR_NUM];
|
||||
static u32 gpc_mf_request_on[IMR_NUM];
|
||||
static DEFINE_SPINLOCK(gpc_lock);
|
||||
|
||||
void imx_gpc_add_m4_wake_up_irq(u32 hwirq, bool enable)
|
||||
{
|
||||
unsigned int idx = hwirq / 32;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
/* Sanity check for SPI irq */
|
||||
if (hwirq < 32)
|
||||
return;
|
||||
|
||||
mask = 1 << hwirq % 32;
|
||||
spin_lock_irqsave(&gpc_lock, flags);
|
||||
gpc_wake_irqs[idx] = enable ? gpc_wake_irqs[idx] | mask :
|
||||
gpc_wake_irqs[idx] & ~mask;
|
||||
spin_unlock_irqrestore(&gpc_lock, flags);
|
||||
}
|
||||
|
||||
void imx_gpc_hold_m4_in_sleep(void)
|
||||
{
|
||||
int val;
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(500);
|
||||
|
||||
/* wait M4 in wfi before asserting hold request */
|
||||
while (!imx_gpc_is_m4_sleeping())
|
||||
if (time_after(jiffies, timeout))
|
||||
pr_err("M4 is NOT in expected sleep!\n");
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_M4_LPSR);
|
||||
val &= ~(GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_MASK <<
|
||||
GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_SHIFT);
|
||||
writel_relaxed(val, gpc_base + GPC_M4_LPSR);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(500);
|
||||
while (readl_relaxed(gpc_base + GPC_M4_LPSR)
|
||||
& (GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_MASK <<
|
||||
GPC_M4_LPSR_M4_SLEEP_HOLD_ACK_SHIFT))
|
||||
if (time_after(jiffies, timeout))
|
||||
pr_err("Wait M4 hold ack timeout!\n");
|
||||
}
|
||||
|
||||
void imx_gpc_release_m4_in_sleep(void)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_M4_LPSR);
|
||||
val |= GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_MASK <<
|
||||
GPC_M4_LPSR_M4_SLEEP_HOLD_REQ_SHIFT;
|
||||
writel_relaxed(val, gpc_base + GPC_M4_LPSR);
|
||||
}
|
||||
|
||||
unsigned int imx_gpc_is_m4_sleeping(void)
|
||||
{
|
||||
if (readl_relaxed(gpc_base + GPC_M4_LPSR) &
|
||||
(GPC_M4_LPSR_M4_SLEEPING_MASK <<
|
||||
GPC_M4_LPSR_M4_SLEEPING_SHIFT))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool imx_gpc_usb_wakeup_enabled(void)
|
||||
{
|
||||
if (!(cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz() || cpu_is_imx6sll()))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* for SoC later than i.MX6SX, USB vbus wakeup
|
||||
* only needs weak 2P5 on, stop_mode_config is
|
||||
* NOT needed, so we check if is USB vbus wakeup
|
||||
* is enabled(assume irq #74 and #75) to decide
|
||||
* if to keep weak 2P5 on.
|
||||
*/
|
||||
if (gpc_wake_irqs[1] & GPC_USB_VBUS_WAKEUP_IRQ_MASK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool imx_gpc_enet_wakeup_enabled(void)
|
||||
{
|
||||
if (!cpu_is_imx6q())
|
||||
return false;
|
||||
|
||||
if (gpc_wake_irqs[3] & GPC_ENET_WAKEUP_IRQ_MASK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int imx_gpc_is_mf_mix_off(void)
|
||||
{
|
||||
return readl_relaxed(gpc_base + GPC_PGC_MF_PDN);
|
||||
}
|
||||
|
||||
static void imx_gpc_mf_mix_off(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++)
|
||||
if (((gpc_wake_irqs[i] | gpc_mf_request_on[i]) &
|
||||
gpc_mf_irqs[i]) != 0)
|
||||
return;
|
||||
|
||||
pr_info("Turn off M/F mix!\n");
|
||||
/* turn off mega/fast mix */
|
||||
writel_relaxed(0x1, gpc_base + GPC_PGC_MF_PDN);
|
||||
}
|
||||
|
||||
void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw)
|
||||
{
|
||||
|
@ -53,9 +184,9 @@ void imx_gpc_set_l2_mem_power_in_lpm(bool power_off)
|
|||
u32 val;
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_CNTR);
|
||||
val &= ~(1 << GPC_CNTR_L2_PGE_SHIFT);
|
||||
val &= ~(1 << GPC_CNTR_L2_PGE);
|
||||
if (power_off)
|
||||
val |= 1 << GPC_CNTR_L2_PGE_SHIFT;
|
||||
val |= 1 << GPC_CNTR_L2_PGE;
|
||||
writel_relaxed(val, gpc_base + GPC_CNTR);
|
||||
}
|
||||
|
||||
|
@ -64,6 +195,11 @@ void imx_gpc_pre_suspend(bool arm_power_off)
|
|||
void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
|
||||
int i;
|
||||
|
||||
/* power down the mega-fast power domain */
|
||||
if ((cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz() || cpu_is_imx6sll()) && arm_power_off)
|
||||
imx_gpc_mf_mix_off();
|
||||
|
||||
/* Tell GPC to power off ARM core when suspend */
|
||||
if (arm_power_off)
|
||||
imx_gpc_set_arm_power_in_lpm(arm_power_off);
|
||||
|
@ -81,6 +217,10 @@ void imx_gpc_post_resume(void)
|
|||
|
||||
/* Keep ARM core powered on for other low-power modes */
|
||||
imx_gpc_set_arm_power_in_lpm(false);
|
||||
/* Keep M/F mix powered on for other low-power modes */
|
||||
if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz() || cpu_is_imx6sll())
|
||||
writel_relaxed(0x0, gpc_base + GPC_PGC_MF_PDN);
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++)
|
||||
writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
|
||||
|
@ -89,11 +229,14 @@ void imx_gpc_post_resume(void)
|
|||
static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on)
|
||||
{
|
||||
unsigned int idx = d->hwirq / 32;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
mask = 1 << d->hwirq % 32;
|
||||
spin_lock_irqsave(&gpc_lock, flags);
|
||||
gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask :
|
||||
gpc_wake_irqs[idx] & ~mask;
|
||||
spin_unlock_irqrestore(&gpc_lock, flags);
|
||||
|
||||
/*
|
||||
* Do *not* call into the parent, as the GIC doesn't have any
|
||||
|
@ -225,11 +368,78 @@ static const struct irq_domain_ops imx_gpc_domain_ops = {
|
|||
.free = irq_domain_free_irqs_common,
|
||||
};
|
||||
|
||||
int imx_gpc_mf_power_on(unsigned int irq, unsigned int on)
|
||||
{
|
||||
struct irq_desc *d = irq_to_desc(irq);
|
||||
unsigned int idx = d->irq_data.hwirq / 32;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
mask = 1 << (d->irq_data.hwirq % 32);
|
||||
spin_lock_irqsave(&gpc_lock, flags);
|
||||
gpc_mf_request_on[idx] = on ? gpc_mf_request_on[idx] | mask :
|
||||
gpc_mf_request_on[idx] & ~mask;
|
||||
spin_unlock_irqrestore(&gpc_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imx_gpc_mf_request_on(unsigned int irq, unsigned int on)
|
||||
{
|
||||
if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz() || cpu_is_imx6sll())
|
||||
return imx_gpc_mf_power_on(irq, on);
|
||||
else if (cpu_is_imx7d())
|
||||
return imx_gpcv2_mf_power_on(irq, on);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_gpc_mf_request_on);
|
||||
|
||||
void imx_gpc_switch_pupscr_clk(bool flag)
|
||||
{
|
||||
static u32 pupscr_sw2iso, pupscr_sw;
|
||||
u32 ratio, pupscr = readl_relaxed(gpc_base + GPC_PGC_CPU_PUPSCR);
|
||||
|
||||
if (flag) {
|
||||
/* save the init clock setting IPG/2048 for IPG@66Mhz */
|
||||
pupscr_sw2iso = (pupscr >> GPC_PGC_CPU_SW2ISO_SHIFT) &
|
||||
GPC_PGC_CPU_SW2ISO_MASK;
|
||||
pupscr_sw = (pupscr >> GPC_PGC_CPU_SW_SHIFT) &
|
||||
GPC_PGC_CPU_SW_MASK;
|
||||
/*
|
||||
* i.MX6UL TO1.0 ARM power up uses IPG/2048 as clock source,
|
||||
* from TO1.1, PGC_CPU_PUPSCR bit [5] is re-defined to switch
|
||||
* clock to IPG/32, enable this bit to speed up the ARM power
|
||||
* up process in low power idle case(IPG@1.5Mhz). So the sw and
|
||||
* sw2iso need to be adjusted as below:
|
||||
* sw_new(sw2iso_new) = (2048 * 1.5 / 66 * 32) * sw(sw2iso)
|
||||
*/
|
||||
ratio = 3072 / (66 * 32);
|
||||
pupscr &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT |
|
||||
GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT);
|
||||
pupscr |= (ratio * pupscr_sw + 1) << GPC_PGC_CPU_SW_SHIFT |
|
||||
1 << 5 | (ratio * pupscr_sw2iso + 1) <<
|
||||
GPC_PGC_CPU_SW2ISO_SHIFT;
|
||||
writel_relaxed(pupscr, gpc_base + GPC_PGC_CPU_PUPSCR);
|
||||
} else {
|
||||
/* restore back after exit from low power idle */
|
||||
pupscr &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT |
|
||||
GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT);
|
||||
pupscr |= pupscr_sw << GPC_PGC_CPU_SW_SHIFT |
|
||||
pupscr_sw2iso << GPC_PGC_CPU_SW2ISO_SHIFT;
|
||||
writel_relaxed(pupscr, gpc_base + GPC_PGC_CPU_PUPSCR);
|
||||
}
|
||||
}
|
||||
|
||||
static int __init imx_gpc_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct irq_domain *parent_domain, *domain;
|
||||
int i;
|
||||
u32 val;
|
||||
u32 cpu_pupscr_sw2iso, cpu_pupscr_sw;
|
||||
u32 cpu_pdnscr_iso2sw, cpu_pdnscr_iso;
|
||||
|
||||
if (!parent) {
|
||||
pr_err("%pOF: no parent, giving up\n", node);
|
||||
|
@ -258,12 +468,70 @@ static int __init imx_gpc_init(struct device_node *node,
|
|||
for (i = 0; i < IMR_NUM; i++)
|
||||
writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4);
|
||||
|
||||
/* Read supported wakeup source in M/F domain */
|
||||
if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull() ||
|
||||
cpu_is_imx6ulz() || cpu_is_imx6sll()) {
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 0,
|
||||
&gpc_mf_irqs[0]);
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 1,
|
||||
&gpc_mf_irqs[1]);
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 2,
|
||||
&gpc_mf_irqs[2]);
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 3,
|
||||
&gpc_mf_irqs[3]);
|
||||
if (!(gpc_mf_irqs[0] | gpc_mf_irqs[1] |
|
||||
gpc_mf_irqs[2] | gpc_mf_irqs[3]))
|
||||
pr_info("No wakeup source in Mega/Fast domain found!\n");
|
||||
}
|
||||
|
||||
/* clear the L2_PGE bit on i.MX6SLL */
|
||||
if (cpu_is_imx6sll()) {
|
||||
val = readl_relaxed(gpc_base + GPC_CNTR);
|
||||
val &= ~(1 << GPC_CNTR_L2_PGE);
|
||||
writel_relaxed(val, gpc_base + GPC_CNTR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the OF_POPULATED flag set in of_irq_init so that
|
||||
* later the GPC power domain driver will not be skipped.
|
||||
*/
|
||||
of_node_clear_flag(node, OF_POPULATED);
|
||||
|
||||
/*
|
||||
* If there are CPU isolation timing settings in dts,
|
||||
* update them according to dts, otherwise, keep them
|
||||
* with default value in registers.
|
||||
*/
|
||||
cpu_pupscr_sw2iso = cpu_pupscr_sw =
|
||||
cpu_pdnscr_iso2sw = cpu_pdnscr_iso = 0;
|
||||
|
||||
/* Read CPU isolation setting for GPC */
|
||||
of_property_read_u32(node, "fsl,cpu_pupscr_sw2iso", &cpu_pupscr_sw2iso);
|
||||
of_property_read_u32(node, "fsl,cpu_pupscr_sw", &cpu_pupscr_sw);
|
||||
of_property_read_u32(node, "fsl,cpu_pdnscr_iso2sw", &cpu_pdnscr_iso2sw);
|
||||
of_property_read_u32(node, "fsl,cpu_pdnscr_iso", &cpu_pdnscr_iso);
|
||||
|
||||
/* Return if no property found in dtb */
|
||||
if ((cpu_pupscr_sw2iso | cpu_pupscr_sw
|
||||
| cpu_pdnscr_iso2sw | cpu_pdnscr_iso) == 0)
|
||||
return 0;
|
||||
|
||||
/* Update CPU PUPSCR timing if it is defined in dts */
|
||||
val = readl_relaxed(gpc_base + GPC_PGC_CPU_PUPSCR);
|
||||
val &= ~(GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT);
|
||||
val &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT);
|
||||
val |= cpu_pupscr_sw2iso << GPC_PGC_CPU_SW2ISO_SHIFT;
|
||||
val |= cpu_pupscr_sw << GPC_PGC_CPU_SW_SHIFT;
|
||||
writel_relaxed(val, gpc_base + GPC_PGC_CPU_PUPSCR);
|
||||
|
||||
/* Update CPU PDNSCR timing if it is defined in dts */
|
||||
val = readl_relaxed(gpc_base + GPC_PGC_CPU_PDNSCR);
|
||||
val &= ~(GPC_PGC_CPU_SW2ISO_MASK << GPC_PGC_CPU_SW2ISO_SHIFT);
|
||||
val &= ~(GPC_PGC_CPU_SW_MASK << GPC_PGC_CPU_SW_SHIFT);
|
||||
val |= cpu_pdnscr_iso2sw << GPC_PGC_CPU_SW2ISO_SHIFT;
|
||||
val |= cpu_pdnscr_iso << GPC_PGC_CPU_SW_SHIFT;
|
||||
writel_relaxed(val, gpc_base + GPC_PGC_CPU_PDNSCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
IRQCHIP_DECLARE(imx_gpc, "fsl,imx6q-gpc", imx_gpc_init);
|
||||
|
|
|
@ -0,0 +1,851 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define IMR_NUM 4
|
||||
#define GPC_MAX_IRQS (IMR_NUM * 32)
|
||||
#define GPC_LPCR_A7_BSC 0x0
|
||||
#define GPC_LPCR_A7_AD 0x4
|
||||
#define GPC_LPCR_M4 0x8
|
||||
#define GPC_SLPCR 0x14
|
||||
#define GPC_MLPCR 0x20
|
||||
#define GPC_PGC_ACK_SEL_A7 0x24
|
||||
#define GPC_MISC 0x2c
|
||||
#define GPC_IMR1_CORE0 0x30
|
||||
#define GPC_IMR1_CORE1 0x40
|
||||
#define GPC_IMR1_M4 0x50
|
||||
#define GPC_SLOT0_CFG 0xb0
|
||||
#define GPC_PGC_CPU_MAPPING 0xec
|
||||
#define GPC_CPU_PGC_SW_PUP_REQ 0xf0
|
||||
#define GPC_PU_PGC_SW_PUP_REQ 0xf8
|
||||
#define GPC_CPU_PGC_SW_PDN_REQ 0xfc
|
||||
#define GPC_PU_PGC_SW_PDN_REQ 0x104
|
||||
#define GPC_GTOR 0x124
|
||||
#define GPC_PGC_C0 0x800
|
||||
#define GPC_PGC_C0_PUPSCR 0x804
|
||||
#define GPC_PGC_SCU_TIMING 0x890
|
||||
#define GPC_PGC_C1 0x840
|
||||
#define GPC_PGC_C1_PUPSCR 0x844
|
||||
#define GPC_PGC_SCU 0x880
|
||||
#define GPC_PGC_FM 0xa00
|
||||
|
||||
#define BM_LPCR_A7_BSC_IRQ_SRC_A7_WAKEUP 0x70000000
|
||||
#define BM_LPCR_A7_BSC_CPU_CLK_ON_LPM 0x4000
|
||||
#define BM_LPCR_A7_BSC_LPM1 0xc
|
||||
#define BM_LPCR_A7_BSC_LPM0 0x3
|
||||
#define BP_LPCR_A7_BSC_LPM1 2
|
||||
#define BP_LPCR_A7_BSC_LPM0 0
|
||||
#define BM_LPCR_M4_MASK_DSM_TRIGGER 0x80000000
|
||||
#define BM_SLPCR_EN_DSM 0x80000000
|
||||
#define BM_SLPCR_RBC_EN 0x40000000
|
||||
#define BM_SLPCR_REG_BYPASS_COUNT 0x3f000000
|
||||
#define BM_SLPCR_VSTBY 0x4
|
||||
#define BM_SLPCR_SBYOS 0x2
|
||||
#define BM_SLPCR_BYPASS_PMIC_READY 0x1
|
||||
#define BM_SLPCR_EN_A7_FASTWUP_WAIT_MODE 0x10000
|
||||
#define BM_LPCR_A7_AD_L2PGE 0x10000
|
||||
#define BM_LPCR_A7_AD_EN_C1_PUP 0x800
|
||||
#define BM_LPCR_A7_AD_EN_C1_IRQ_PUP 0x400
|
||||
#define BM_LPCR_A7_AD_EN_C0_PUP 0x200
|
||||
#define BM_LPCR_A7_AD_EN_C0_IRQ_PUP 0x100
|
||||
#define BM_LPCR_A7_AD_EN_PLAT_PDN 0x10
|
||||
#define BM_LPCR_A7_AD_EN_C1_PDN 0x8
|
||||
#define BM_LPCR_A7_AD_EN_C1_WFI_PDN 0x4
|
||||
#define BM_LPCR_A7_AD_EN_C0_PDN 0x2
|
||||
#define BM_LPCR_A7_AD_EN_C0_WFI_PDN 0x1
|
||||
|
||||
#define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 0x2
|
||||
#define BM_GPC_PGC_PCG 0x1
|
||||
#define BM_GPC_PGC_CORE_PUPSCR 0x7fff80
|
||||
|
||||
#define BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK 0x80000000
|
||||
#define BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK 0x8000
|
||||
#define BM_GPC_MLPCR_MEMLP_CTL_DIS 0x1
|
||||
|
||||
#define BP_LPCR_A7_BSC_IRQ_SRC 28
|
||||
|
||||
#define MAX_SLOT_NUMBER 10
|
||||
#define A7_LPM_WAIT 0x5
|
||||
#define A7_LPM_STOP 0xa
|
||||
|
||||
enum imx_gpc_slot {
|
||||
CORE0_A7,
|
||||
CORE1_A7,
|
||||
SCU_A7,
|
||||
FAST_MEGA_MIX,
|
||||
MIPI_PHY,
|
||||
PCIE_PHY,
|
||||
USB_OTG1_PHY,
|
||||
USB_OTG2_PHY,
|
||||
USB_HSIC_PHY,
|
||||
CORE0_M4,
|
||||
};
|
||||
|
||||
static void __iomem *gpc_base;
|
||||
static u32 gpcv2_wake_irqs[IMR_NUM];
|
||||
static u32 gpcv2_saved_imrs[IMR_NUM];
|
||||
static u32 gpcv2_saved_imrs_m4[IMR_NUM];
|
||||
static u32 gpcv2_mf_irqs[IMR_NUM];
|
||||
static u32 gpcv2_mf_request_on[IMR_NUM];
|
||||
static DEFINE_SPINLOCK(gpcv2_lock);
|
||||
|
||||
void imx_gpcv2_add_m4_wake_up_irq(u32 hwirq, bool enable)
|
||||
{
|
||||
unsigned int idx = hwirq / 32;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
/* Sanity check for SPI irq */
|
||||
if (hwirq < 32)
|
||||
return;
|
||||
|
||||
mask = 1 << hwirq % 32;
|
||||
spin_lock_irqsave(&gpcv2_lock, flags);
|
||||
gpcv2_wake_irqs[idx] = enable ? gpcv2_wake_irqs[idx] | mask :
|
||||
gpcv2_wake_irqs[idx] & ~mask;
|
||||
spin_unlock_irqrestore(&gpcv2_lock, flags);
|
||||
}
|
||||
|
||||
static int imx_gpcv2_irq_set_wake(struct irq_data *d, unsigned int on)
|
||||
{
|
||||
unsigned int idx = d->hwirq / 32;
|
||||
unsigned long flags;
|
||||
u32 mask;
|
||||
|
||||
BUG_ON(idx >= IMR_NUM);
|
||||
|
||||
mask = 1 << d->hwirq % 32;
|
||||
spin_lock_irqsave(&gpcv2_lock, flags);
|
||||
gpcv2_wake_irqs[idx] = on ? gpcv2_wake_irqs[idx] | mask :
|
||||
gpcv2_wake_irqs[idx] & ~mask;
|
||||
spin_unlock_irqrestore(&gpcv2_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void imx_gpcv2_mask_all(void)
|
||||
{
|
||||
void __iomem *reg_imr1 = gpc_base + GPC_IMR1_CORE0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++) {
|
||||
gpcv2_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4);
|
||||
writel_relaxed(~0, reg_imr1 + i * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void imx_gpcv2_restore_all(void)
|
||||
{
|
||||
void __iomem *reg_imr1 = gpc_base + GPC_IMR1_CORE0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++)
|
||||
writel_relaxed(gpcv2_saved_imrs[i], reg_imr1 + i * 4);
|
||||
}
|
||||
|
||||
void imx_gpcv2_hwirq_unmask(unsigned int hwirq)
|
||||
{
|
||||
void __iomem *reg;
|
||||
u32 val;
|
||||
|
||||
reg = gpc_base + GPC_IMR1_CORE0 + (hwirq / 32) * 4;
|
||||
val = readl_relaxed(reg);
|
||||
val &= ~(1 << hwirq % 32);
|
||||
writel_relaxed(val, reg);
|
||||
}
|
||||
|
||||
void imx_gpcv2_hwirq_mask(unsigned int hwirq)
|
||||
{
|
||||
void __iomem *reg;
|
||||
u32 val;
|
||||
|
||||
reg = gpc_base + GPC_IMR1_CORE0 + (hwirq / 32) * 4;
|
||||
val = readl_relaxed(reg);
|
||||
val |= 1 << (hwirq % 32);
|
||||
writel_relaxed(val, reg);
|
||||
}
|
||||
|
||||
static void imx_gpcv2_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
imx_gpcv2_hwirq_unmask(d->hwirq);
|
||||
irq_chip_unmask_parent(d);
|
||||
}
|
||||
|
||||
static void imx_gpcv2_irq_mask(struct irq_data *d)
|
||||
{
|
||||
imx_gpcv2_hwirq_mask(d->hwirq);
|
||||
irq_chip_mask_parent(d);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_slot_ack(u32 index, enum imx_gpc_slot m_core,
|
||||
bool mode, bool ack)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (index >= MAX_SLOT_NUMBER)
|
||||
pr_err("Invalid slot index!\n");
|
||||
/* set slot */
|
||||
writel_relaxed(readl_relaxed(gpc_base + GPC_SLOT0_CFG + index * 4) |
|
||||
((mode + 1) << (m_core * 2)),
|
||||
gpc_base + GPC_SLOT0_CFG + index * 4);
|
||||
|
||||
if (ack) {
|
||||
/* set ack */
|
||||
val = readl_relaxed(gpc_base + GPC_PGC_ACK_SEL_A7);
|
||||
/* clear dummy ack */
|
||||
val &= ~(1 << (15 + (mode ? 16 : 0)));
|
||||
val |= 1 << (m_core + (mode ? 16 : 0));
|
||||
writel_relaxed(val, gpc_base + GPC_PGC_ACK_SEL_A7);
|
||||
}
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_lpm_mode(enum mxc_cpu_pwr_mode mode)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val1, val2;
|
||||
|
||||
spin_lock_irqsave(&gpcv2_lock, flags);
|
||||
|
||||
val1 = readl_relaxed(gpc_base + GPC_LPCR_A7_BSC);
|
||||
val2 = readl_relaxed(gpc_base + GPC_SLPCR);
|
||||
|
||||
/* all cores' LPM settings must be same */
|
||||
val1 &= ~(BM_LPCR_A7_BSC_LPM0 | BM_LPCR_A7_BSC_LPM1);
|
||||
|
||||
val1 |= BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
|
||||
|
||||
val2 &= ~(BM_SLPCR_EN_DSM | BM_SLPCR_VSTBY | BM_SLPCR_RBC_EN |
|
||||
BM_SLPCR_SBYOS | BM_SLPCR_BYPASS_PMIC_READY);
|
||||
/*
|
||||
* GPC: When improper low-power sequence is used,
|
||||
* the SoC enters low power mode before the ARM core executes WFI.
|
||||
*
|
||||
* Software workaround:
|
||||
* 1) Software should trigger IRQ #32 (IOMUX) to be always pending
|
||||
* by setting IOMUX_GPR1_IRQ.
|
||||
* 2) Software should then unmask IRQ #32 in GPC before setting GPC
|
||||
* Low-Power mode.
|
||||
* 3) Software should mask IRQ #32 right after GPC Low-Power mode
|
||||
* is set.
|
||||
*/
|
||||
switch (mode) {
|
||||
case WAIT_CLOCKED:
|
||||
imx_gpcv2_hwirq_unmask(0);
|
||||
break;
|
||||
case WAIT_UNCLOCKED:
|
||||
val1 |= A7_LPM_WAIT << BP_LPCR_A7_BSC_LPM0;
|
||||
val1 &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
|
||||
imx_gpcv2_hwirq_mask(0);
|
||||
break;
|
||||
case STOP_POWER_ON:
|
||||
val1 |= A7_LPM_STOP << BP_LPCR_A7_BSC_LPM0;
|
||||
val1 &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
|
||||
val2 |= BM_SLPCR_EN_DSM;
|
||||
val2 |= BM_SLPCR_RBC_EN;
|
||||
val2 |= BM_SLPCR_BYPASS_PMIC_READY;
|
||||
imx_gpcv2_hwirq_mask(0);
|
||||
break;
|
||||
case STOP_POWER_OFF:
|
||||
val1 |= A7_LPM_STOP << BP_LPCR_A7_BSC_LPM0;
|
||||
val1 &= ~BM_LPCR_A7_BSC_CPU_CLK_ON_LPM;
|
||||
val2 |= BM_SLPCR_EN_DSM;
|
||||
val2 |= BM_SLPCR_RBC_EN;
|
||||
val2 |= BM_SLPCR_SBYOS;
|
||||
val2 |= BM_SLPCR_VSTBY;
|
||||
val2 |= BM_SLPCR_BYPASS_PMIC_READY;
|
||||
imx_gpcv2_hwirq_mask(0);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
writel_relaxed(val1, gpc_base + GPC_LPCR_A7_BSC);
|
||||
writel_relaxed(val2, gpc_base + GPC_SLPCR);
|
||||
|
||||
spin_unlock_irqrestore(&gpcv2_lock, flags);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_plat_power_gate_by_lpm(bool pdn)
|
||||
{
|
||||
u32 val = readl_relaxed(gpc_base + GPC_LPCR_A7_AD);
|
||||
|
||||
val &= ~(BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE);
|
||||
if (pdn)
|
||||
val |= BM_LPCR_A7_AD_EN_PLAT_PDN | BM_LPCR_A7_AD_L2PGE;
|
||||
|
||||
writel_relaxed(val, gpc_base + GPC_LPCR_A7_AD);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_m_core_pgc(bool enable, u32 offset)
|
||||
{
|
||||
u32 val = readl_relaxed(gpc_base + offset) & (~BM_GPC_PGC_PCG);
|
||||
|
||||
if (enable)
|
||||
val |= BM_GPC_PGC_PCG;
|
||||
|
||||
writel_relaxed(val, gpc_base + offset);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn)
|
||||
{
|
||||
u32 val = readl_relaxed(gpc_base + (pdn ?
|
||||
GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ));
|
||||
|
||||
imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C1);
|
||||
val |= BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7;
|
||||
writel_relaxed(val, gpc_base + (pdn ?
|
||||
GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ));
|
||||
|
||||
while ((readl_relaxed(gpc_base + (pdn ?
|
||||
GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ)) &
|
||||
BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7) != 0)
|
||||
;
|
||||
imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C1);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_cpu_power_gate_by_wfi(u32 cpu, bool pdn)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&gpcv2_lock, flags);
|
||||
val = readl_relaxed(gpc_base + GPC_LPCR_A7_AD);
|
||||
|
||||
if (cpu == 0) {
|
||||
if (pdn) {
|
||||
imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C0);
|
||||
val |= BM_LPCR_A7_AD_EN_C0_WFI_PDN |
|
||||
BM_LPCR_A7_AD_EN_C0_IRQ_PUP;
|
||||
} else {
|
||||
imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C0);
|
||||
val &= ~(BM_LPCR_A7_AD_EN_C0_WFI_PDN |
|
||||
BM_LPCR_A7_AD_EN_C0_IRQ_PUP);
|
||||
}
|
||||
}
|
||||
if (cpu == 1) {
|
||||
if (pdn) {
|
||||
imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C1);
|
||||
val |= BM_LPCR_A7_AD_EN_C1_WFI_PDN |
|
||||
BM_LPCR_A7_AD_EN_C1_IRQ_PUP;
|
||||
} else {
|
||||
imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C1);
|
||||
val &= ~(BM_LPCR_A7_AD_EN_C1_WFI_PDN |
|
||||
BM_LPCR_A7_AD_EN_C1_IRQ_PUP);
|
||||
}
|
||||
}
|
||||
writel_relaxed(val, gpc_base + GPC_LPCR_A7_AD);
|
||||
spin_unlock_irqrestore(&gpcv2_lock, flags);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_cpu_power_gate_by_lpm(u32 cpu, bool pdn)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(&gpcv2_lock, flags);
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_LPCR_A7_AD);
|
||||
if (cpu == 0) {
|
||||
if (pdn)
|
||||
val |= BM_LPCR_A7_AD_EN_C0_PDN |
|
||||
BM_LPCR_A7_AD_EN_C0_PUP;
|
||||
else
|
||||
val &= ~(BM_LPCR_A7_AD_EN_C0_PDN |
|
||||
BM_LPCR_A7_AD_EN_C0_PUP);
|
||||
}
|
||||
if (cpu == 1) {
|
||||
if (pdn)
|
||||
val |= BM_LPCR_A7_AD_EN_C1_PDN |
|
||||
BM_LPCR_A7_AD_EN_C1_PUP;
|
||||
else
|
||||
val &= ~(BM_LPCR_A7_AD_EN_C1_PDN |
|
||||
BM_LPCR_A7_AD_EN_C1_PUP);
|
||||
}
|
||||
|
||||
writel_relaxed(val, gpc_base + GPC_LPCR_A7_AD);
|
||||
spin_unlock_irqrestore(&gpcv2_lock, flags);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_cpu_power_gate_in_idle(bool pdn)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
imx_gpcv2_set_cpu_power_gate_by_lpm(cpu, pdn);
|
||||
|
||||
spin_lock_irqsave(&gpcv2_lock, flags);
|
||||
|
||||
imx_gpcv2_set_m_core_pgc(pdn, GPC_PGC_C0);
|
||||
if (num_online_cpus() > 1)
|
||||
imx_gpcv2_set_m_core_pgc(pdn, GPC_PGC_C1);
|
||||
imx_gpcv2_set_m_core_pgc(pdn, GPC_PGC_SCU);
|
||||
imx_gpcv2_set_plat_power_gate_by_lpm(pdn);
|
||||
|
||||
if (pdn) {
|
||||
imx_gpcv2_set_slot_ack(0, CORE0_A7, false, false);
|
||||
if (num_online_cpus() > 1)
|
||||
imx_gpcv2_set_slot_ack(2, CORE1_A7, false, false);
|
||||
imx_gpcv2_set_slot_ack(3, SCU_A7, false, true);
|
||||
imx_gpcv2_set_slot_ack(6, SCU_A7, true, false);
|
||||
if (num_online_cpus() > 1)
|
||||
imx_gpcv2_set_slot_ack(6, CORE1_A7, true, false);
|
||||
imx_gpcv2_set_slot_ack(6, CORE0_A7, true, true);
|
||||
} else {
|
||||
writel_relaxed(0x0, gpc_base + GPC_SLOT0_CFG + 0 * 0x4);
|
||||
writel_relaxed(0x0, gpc_base + GPC_SLOT0_CFG + 2 * 0x4);
|
||||
writel_relaxed(0x0, gpc_base + GPC_SLOT0_CFG + 3 * 0x4);
|
||||
writel_relaxed(0x0, gpc_base + GPC_SLOT0_CFG + 6 * 0x4);
|
||||
writel_relaxed(0x0, gpc_base + GPC_SLOT0_CFG + 7 * 0x4);
|
||||
writel_relaxed(0x0, gpc_base + GPC_SLOT0_CFG + 8 * 0x4);
|
||||
writel_relaxed(BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK |
|
||||
BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK,
|
||||
gpc_base + GPC_PGC_ACK_SEL_A7);
|
||||
imx_gpcv2_enable_rbc(false);
|
||||
}
|
||||
spin_unlock_irqrestore(&gpcv2_lock, flags);
|
||||
}
|
||||
|
||||
void imx_gpcv2_set_mix_phy_gate_by_lpm(u32 pdn_index, u32 pup_index)
|
||||
{
|
||||
/* set power down slot */
|
||||
writel_relaxed(1 << (FAST_MEGA_MIX * 2),
|
||||
gpc_base + GPC_SLOT0_CFG + pdn_index * 4);
|
||||
|
||||
/* set power up slot */
|
||||
writel_relaxed(1 << (FAST_MEGA_MIX * 2 + 1),
|
||||
gpc_base + GPC_SLOT0_CFG + pup_index * 4);
|
||||
}
|
||||
|
||||
unsigned int imx_gpcv2_is_mf_mix_off(void)
|
||||
{
|
||||
return readl_relaxed(gpc_base + GPC_PGC_FM);
|
||||
}
|
||||
|
||||
static void imx_gpcv2_mf_mix_off(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++)
|
||||
if (((gpcv2_wake_irqs[i] | gpcv2_mf_request_on[i]) &
|
||||
gpcv2_mf_irqs[i]) != 0)
|
||||
return;
|
||||
|
||||
pr_info("Turn off Mega/Fast mix in DSM\n");
|
||||
imx_gpcv2_set_slot_ack(1, FAST_MEGA_MIX, false, false);
|
||||
imx_gpcv2_set_slot_ack(5, FAST_MEGA_MIX, true, false);
|
||||
imx_gpcv2_set_m_core_pgc(true, GPC_PGC_FM);
|
||||
}
|
||||
|
||||
int imx_gpcv2_mf_power_on(unsigned int irq, unsigned int on)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
unsigned long hwirq = desc->irq_data.hwirq;
|
||||
unsigned int idx = hwirq / 32;
|
||||
unsigned long flags;
|
||||
u32 mask = 1 << (hwirq % 32);
|
||||
|
||||
BUG_ON(idx >= IMR_NUM);
|
||||
|
||||
spin_lock_irqsave(&gpcv2_lock, flags);
|
||||
gpcv2_mf_request_on[idx] = on ? gpcv2_mf_request_on[idx] | mask :
|
||||
gpcv2_mf_request_on[idx] & ~mask;
|
||||
spin_unlock_irqrestore(&gpcv2_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void imx_gpcv2_enable_rbc(bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* need to mask all interrupts in GPC before
|
||||
* operating RBC configurations
|
||||
*/
|
||||
imx_gpcv2_mask_all();
|
||||
|
||||
/* configure RBC enable bit */
|
||||
val = readl_relaxed(gpc_base + GPC_SLPCR);
|
||||
val &= ~BM_SLPCR_RBC_EN;
|
||||
val |= enable ? BM_SLPCR_RBC_EN : 0;
|
||||
writel_relaxed(val, gpc_base + GPC_SLPCR);
|
||||
|
||||
/* configure RBC count */
|
||||
val = readl_relaxed(gpc_base + GPC_SLPCR);
|
||||
val &= ~BM_SLPCR_REG_BYPASS_COUNT;
|
||||
val |= enable ? BM_SLPCR_REG_BYPASS_COUNT : 0;
|
||||
writel(val, gpc_base + GPC_SLPCR);
|
||||
|
||||
/*
|
||||
* need to delay at least 2 cycles of CKIL(32K)
|
||||
* due to hardware design requirement, which is
|
||||
* ~61us, here we use 65us for safe
|
||||
*/
|
||||
udelay(65);
|
||||
|
||||
/* restore GPC interrupt mask settings */
|
||||
imx_gpcv2_restore_all();
|
||||
}
|
||||
|
||||
|
||||
void imx_gpcv2_pre_suspend(bool arm_power_off)
|
||||
{
|
||||
void __iomem *reg_imr1 = gpc_base + GPC_IMR1_CORE0;
|
||||
int i;
|
||||
|
||||
if (arm_power_off) {
|
||||
imx_gpcv2_set_lpm_mode(STOP_POWER_OFF);
|
||||
/* enable core0 power down/up with low power mode */
|
||||
imx_gpcv2_set_cpu_power_gate_by_lpm(0, true);
|
||||
/* enable plat power down with low power mode */
|
||||
imx_gpcv2_set_plat_power_gate_by_lpm(true);
|
||||
|
||||
/*
|
||||
* To avoid confuse, we use slot 0~4 for power down,
|
||||
* slot 5~9 for power up.
|
||||
*
|
||||
* Power down slot sequence:
|
||||
* Slot0 -> CORE0
|
||||
* Slot1 -> Mega/Fast MIX
|
||||
* Slot2 -> SCU
|
||||
*
|
||||
* Power up slot sequence:
|
||||
* Slot5 -> Mega/Fast MIX
|
||||
* Slot6 -> SCU
|
||||
* Slot7 -> CORE0
|
||||
*/
|
||||
imx_gpcv2_set_slot_ack(0, CORE0_A7, false, false);
|
||||
imx_gpcv2_set_slot_ack(2, SCU_A7, false, true);
|
||||
|
||||
if ((!imx_src_is_m4_enabled()) ||
|
||||
(imx_src_is_m4_enabled() && imx_mu_is_m4_in_stop()))
|
||||
imx_gpcv2_mf_mix_off();;
|
||||
|
||||
imx_gpcv2_set_slot_ack(6, SCU_A7, true, false);
|
||||
imx_gpcv2_set_slot_ack(6, CORE0_A7, true, true);
|
||||
|
||||
/* enable core0, scu */
|
||||
imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C0);
|
||||
imx_gpcv2_set_m_core_pgc(true, GPC_PGC_SCU);
|
||||
} else {
|
||||
imx_gpcv2_set_lpm_mode(STOP_POWER_ON);
|
||||
}
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++) {
|
||||
gpcv2_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4);
|
||||
writel_relaxed(~gpcv2_wake_irqs[i], reg_imr1 + i * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void imx_gpcv2_enable_wakeup_for_m4(void)
|
||||
{
|
||||
void __iomem *reg_imr2 = gpc_base + GPC_IMR1_M4;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++) {
|
||||
gpcv2_saved_imrs_m4[i] = readl_relaxed(reg_imr2 + i * 4);
|
||||
writel_relaxed(~gpcv2_wake_irqs[i], reg_imr2 + i * 4);
|
||||
}
|
||||
}
|
||||
|
||||
void imx_gpcv2_disable_wakeup_for_m4(void)
|
||||
{
|
||||
void __iomem *reg_imr2 = gpc_base + GPC_IMR1_M4;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++)
|
||||
writel_relaxed(gpcv2_saved_imrs_m4[i], reg_imr2 + i * 4);
|
||||
}
|
||||
|
||||
void imx_gpcv2_post_resume(void)
|
||||
{
|
||||
void __iomem *reg_imr1 = gpc_base + GPC_IMR1_CORE0;
|
||||
int i, val;
|
||||
|
||||
/* only external IRQs to wake up LPM and core 0/1 */
|
||||
val = readl_relaxed(gpc_base + GPC_LPCR_A7_BSC);
|
||||
val |= BM_LPCR_A7_BSC_IRQ_SRC_A7_WAKEUP;
|
||||
writel_relaxed(val, gpc_base + GPC_LPCR_A7_BSC);
|
||||
/* mask m4 dsm trigger if M4 NOT enabled */
|
||||
if (!imx_src_is_m4_enabled())
|
||||
writel_relaxed(readl_relaxed(gpc_base + GPC_LPCR_M4) |
|
||||
BM_LPCR_M4_MASK_DSM_TRIGGER, gpc_base + GPC_LPCR_M4);
|
||||
/* set mega/fast mix in A7 domain */
|
||||
writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_MAPPING);
|
||||
/* set SCU timing */
|
||||
writel_relaxed((0x59 << 10) | 0x5B | (0x2 << 20),
|
||||
gpc_base + GPC_PGC_SCU_TIMING);
|
||||
|
||||
/* set C0/C1 power up timming per design requirement */
|
||||
val = readl_relaxed(gpc_base + GPC_PGC_C0_PUPSCR);
|
||||
val &= ~BM_GPC_PGC_CORE_PUPSCR;
|
||||
val |= (0x1A << 7);
|
||||
writel_relaxed(val, gpc_base + GPC_PGC_C0_PUPSCR);
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_PGC_C1_PUPSCR);
|
||||
val &= ~BM_GPC_PGC_CORE_PUPSCR;
|
||||
val |= (0x1A << 7);
|
||||
writel_relaxed(val, gpc_base + GPC_PGC_C1_PUPSCR);
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_SLPCR);
|
||||
val &= ~(BM_SLPCR_EN_DSM);
|
||||
if (!imx_src_is_m4_enabled())
|
||||
val &= ~(BM_SLPCR_VSTBY | BM_SLPCR_RBC_EN |
|
||||
BM_SLPCR_SBYOS | BM_SLPCR_BYPASS_PMIC_READY);
|
||||
val |= BM_SLPCR_EN_A7_FASTWUP_WAIT_MODE;
|
||||
writel_relaxed(val, gpc_base + GPC_SLPCR);
|
||||
|
||||
if (imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) {
|
||||
/* disable memory low power mode */
|
||||
val = readl_relaxed(gpc_base + GPC_MLPCR);
|
||||
val |= BM_GPC_MLPCR_MEMLP_CTL_DIS;
|
||||
writel_relaxed(val, gpc_base + GPC_MLPCR);
|
||||
}
|
||||
|
||||
for (i = 0; i < IMR_NUM; i++)
|
||||
writel_relaxed(gpcv2_saved_imrs[i], reg_imr1 + i * 4);
|
||||
|
||||
imx_gpcv2_set_lpm_mode(WAIT_CLOCKED);
|
||||
imx_gpcv2_set_cpu_power_gate_by_lpm(0, false);
|
||||
imx_gpcv2_set_plat_power_gate_by_lpm(false);
|
||||
|
||||
imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C0);
|
||||
imx_gpcv2_set_m_core_pgc(false, GPC_PGC_SCU);
|
||||
imx_gpcv2_set_m_core_pgc(false, GPC_PGC_FM);
|
||||
for (i = 0; i < MAX_SLOT_NUMBER; i++){
|
||||
if (i == 1 || i == 5) /* skip slts m4 uses */
|
||||
continue;
|
||||
writel_relaxed(0x0, gpc_base + GPC_SLOT0_CFG + i * 0x4);
|
||||
}
|
||||
writel_relaxed(BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK |
|
||||
BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK,
|
||||
gpc_base + GPC_PGC_ACK_SEL_A7);
|
||||
|
||||
/* disable RBC */
|
||||
imx_gpcv2_enable_rbc(false);
|
||||
}
|
||||
|
||||
static struct irq_chip imx_gpcv2_chip = {
|
||||
.name = "GPCV2",
|
||||
.irq_eoi = irq_chip_eoi_parent,
|
||||
.irq_mask = imx_gpcv2_irq_mask,
|
||||
.irq_unmask = imx_gpcv2_irq_unmask,
|
||||
.irq_retrigger = irq_chip_retrigger_hierarchy,
|
||||
.irq_set_wake = imx_gpcv2_irq_set_wake,
|
||||
#ifdef CONFIG_SMP
|
||||
.irq_set_affinity = irq_chip_set_affinity_parent,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int imx_gpcv2_domain_xlate(struct irq_domain *domain,
|
||||
struct device_node *controller,
|
||||
const u32 *intspec,
|
||||
unsigned int intsize,
|
||||
unsigned long *out_hwirq,
|
||||
unsigned int *out_type)
|
||||
{
|
||||
if (irq_domain_get_of_node(domain) != controller)
|
||||
return -EINVAL; /* Shouldn't happen, really... */
|
||||
if (intsize != 3)
|
||||
return -EINVAL; /* Not GIC compliant */
|
||||
if (intspec[0] != 0)
|
||||
return -EINVAL; /* No PPI should point to this domain */
|
||||
|
||||
*out_hwirq = intspec[1];
|
||||
*out_type = intspec[2];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
|
||||
unsigned int irq,
|
||||
unsigned int nr_irqs, void *data)
|
||||
{
|
||||
struct irq_fwspec *fwspec = data;
|
||||
struct irq_fwspec parent_fwspec;
|
||||
irq_hw_number_t hwirq;
|
||||
int i;
|
||||
|
||||
if (fwspec->param_count != 3)
|
||||
return -EINVAL; /* Not GIC compliant */
|
||||
if (fwspec->param[0] != 0)
|
||||
return -EINVAL; /* No PPI should point to this domain */
|
||||
|
||||
hwirq = fwspec->param[1];
|
||||
if (hwirq >= GPC_MAX_IRQS)
|
||||
return -EINVAL; /* Can't deal with this */
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
|
||||
&imx_gpcv2_chip, NULL);
|
||||
|
||||
parent_fwspec.fwnode = domain->parent->fwnode;
|
||||
parent_fwspec.param_count = 3;
|
||||
parent_fwspec.param[0] = 0;
|
||||
parent_fwspec.param[1] = hwirq;
|
||||
parent_fwspec.param[2] = fwspec->param[2];
|
||||
|
||||
return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
|
||||
&parent_fwspec);
|
||||
}
|
||||
|
||||
static struct irq_domain_ops imx_gpcv2_domain_ops = {
|
||||
.xlate = imx_gpcv2_domain_xlate,
|
||||
.alloc = imx_gpcv2_domain_alloc,
|
||||
.free = irq_domain_free_irqs_common,
|
||||
};
|
||||
|
||||
static int __init imx_gpcv2_init(struct device_node *node,
|
||||
struct device_node *parent)
|
||||
{
|
||||
struct irq_domain *parent_domain, *domain;
|
||||
int i, val;
|
||||
|
||||
if (!parent) {
|
||||
pr_err("%s: no parent, giving up\n", node->full_name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parent_domain = irq_find_host(parent);
|
||||
if (!parent_domain) {
|
||||
pr_err("%s: unable to obtain parent domain\n", node->full_name);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
gpc_base = of_iomap(node, 0);
|
||||
if (WARN_ON(!gpc_base))
|
||||
return -ENOMEM;
|
||||
|
||||
domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
|
||||
node, &imx_gpcv2_domain_ops,
|
||||
NULL);
|
||||
if (!domain) {
|
||||
iounmap(gpc_base);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Initially mask all interrupts */
|
||||
for (i = 0; i < IMR_NUM; i++) {
|
||||
writel_relaxed(~0, gpc_base + GPC_IMR1_CORE0 + i * 4);
|
||||
writel_relaxed(~0, gpc_base + GPC_IMR1_CORE1 + i * 4);
|
||||
}
|
||||
/*
|
||||
* Due to hardware design requirement, need to make sure GPR
|
||||
* interrupt(#32) is unmasked during RUN mode to avoid entering
|
||||
* DSM by mistake.
|
||||
*/
|
||||
writel_relaxed(~0x1, gpc_base + GPC_IMR1_CORE0);
|
||||
|
||||
/* Read supported wakeup source in M/F domain */
|
||||
if (cpu_is_imx7d()) {
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 0,
|
||||
&gpcv2_mf_irqs[0]);
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 1,
|
||||
&gpcv2_mf_irqs[1]);
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 2,
|
||||
&gpcv2_mf_irqs[2]);
|
||||
of_property_read_u32_index(node, "fsl,mf-mix-wakeup-irq", 3,
|
||||
&gpcv2_mf_irqs[3]);
|
||||
if (!(gpcv2_mf_irqs[0] | gpcv2_mf_irqs[1] |
|
||||
gpcv2_mf_irqs[2] | gpcv2_mf_irqs[3]))
|
||||
pr_info("No wakeup source in Mega/Fast domain found!\n");
|
||||
}
|
||||
|
||||
/* only external IRQs to wake up LPM and core 0/1 */
|
||||
val = readl_relaxed(gpc_base + GPC_LPCR_A7_BSC);
|
||||
val |= BM_LPCR_A7_BSC_IRQ_SRC_A7_WAKEUP;
|
||||
writel_relaxed(val, gpc_base + GPC_LPCR_A7_BSC);
|
||||
/* mask m4 dsm trigger if M4 NOT enabled */
|
||||
if (!imx_src_is_m4_enabled())
|
||||
writel_relaxed(readl_relaxed(gpc_base + GPC_LPCR_M4) |
|
||||
BM_LPCR_M4_MASK_DSM_TRIGGER, gpc_base + GPC_LPCR_M4);
|
||||
/* set mega/fast mix in A7 domain */
|
||||
writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_MAPPING);
|
||||
/* set SCU timing */
|
||||
writel_relaxed((0x59 << 10) | 0x5B | (0x2 << 20),
|
||||
gpc_base + GPC_PGC_SCU_TIMING);
|
||||
|
||||
/* set C0/C1 power up timming per design requirement */
|
||||
val = readl_relaxed(gpc_base + GPC_PGC_C0_PUPSCR);
|
||||
val &= ~BM_GPC_PGC_CORE_PUPSCR;
|
||||
val |= (0x1A << 7);
|
||||
writel_relaxed(val, gpc_base + GPC_PGC_C0_PUPSCR);
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_PGC_C1_PUPSCR);
|
||||
val &= ~BM_GPC_PGC_CORE_PUPSCR;
|
||||
val |= (0x1A << 7);
|
||||
writel_relaxed(val, gpc_base + GPC_PGC_C1_PUPSCR);
|
||||
|
||||
writel_relaxed(BM_GPC_PGC_ACK_SEL_A7_DUMMY_PUP_ACK |
|
||||
BM_GPC_PGC_ACK_SEL_A7_DUMMY_PDN_ACK,
|
||||
gpc_base + GPC_PGC_ACK_SEL_A7);
|
||||
|
||||
val = readl_relaxed(gpc_base + GPC_SLPCR);
|
||||
val &= ~(BM_SLPCR_EN_DSM);
|
||||
if (!imx_src_is_m4_enabled())
|
||||
val &= ~(BM_SLPCR_VSTBY | BM_SLPCR_RBC_EN |
|
||||
BM_SLPCR_SBYOS | BM_SLPCR_BYPASS_PMIC_READY);
|
||||
val |= BM_SLPCR_EN_A7_FASTWUP_WAIT_MODE;
|
||||
writel_relaxed(val, gpc_base + GPC_SLPCR);
|
||||
|
||||
if (imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) {
|
||||
/* disable memory low power mode */
|
||||
val = readl_relaxed(gpc_base + GPC_MLPCR);
|
||||
val |= BM_GPC_MLPCR_MEMLP_CTL_DIS;
|
||||
writel_relaxed(val, gpc_base + GPC_MLPCR);
|
||||
}
|
||||
|
||||
/* disable RBC */
|
||||
imx_gpcv2_enable_rbc(false);
|
||||
|
||||
/*
|
||||
* Clear the OF_POPULATED flag set in of_irq_init so that
|
||||
* later the GPC power domain driver will not be skipped.
|
||||
*/
|
||||
of_node_clear_flag(node, OF_POPULATED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We cannot use the IRQCHIP_DECLARE macro that lives in
|
||||
* drivers/irqchip, so we're forced to roll our own. Not very nice.
|
||||
*/
|
||||
OF_DECLARE_2(irqchip, imx_gpcv2, "fsl,imx7d-gpc", imx_gpcv2_init);
|
||||
|
||||
void __init imx_gpcv2_check_dt(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-gpc");
|
||||
if (WARN_ON(!np))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL))) {
|
||||
pr_warn("Outdated DT detected, suspend/resume will NOT work\n");
|
||||
|
||||
/* map GPC, so that at least CPUidle and WARs keep working */
|
||||
gpc_base = of_iomap(np, 0);
|
||||
}
|
||||
}
|
|
@ -81,13 +81,16 @@
|
|||
* CCM 0x020c4000+0x004000 -> 0xf42c4000+0x004000
|
||||
* ANATOP 0x020c8000+0x004000 -> 0xf42c8000+0x004000
|
||||
* UART4 0x021f0000+0x004000 -> 0xf42f0000+0x004000
|
||||
* mx7d:
|
||||
* CCM 0x30380000+0x010000 -> 0xf5380000+0x010000
|
||||
* ANATOP 0x30360000+0x010000 -> 0xf5360000+0x010000
|
||||
* UART1 0x30860000+0x010000 -> 0xf5860000+0x010000
|
||||
*/
|
||||
#define IMX_IO_P2V(x) ( \
|
||||
(((x) & 0x80000000) >> 7) | \
|
||||
(0xf4000000 + \
|
||||
(((x) & 0x50000000) >> 6) + \
|
||||
(((x) & 0x0b000000) >> 4) + \
|
||||
(((x) & 0x000fffff))))
|
||||
(((x) & 0x50000000) >> 4) + \
|
||||
(((x) & 0x0a000000) >> 4) + \
|
||||
(((x) & 0x00ffffff))))
|
||||
|
||||
#define IMX_IO_ADDRESS(x) IOMEM(IMX_IO_P2V(x))
|
||||
|
||||
|
@ -99,6 +102,9 @@
|
|||
#include "mx2x.h"
|
||||
#include "mx21.h"
|
||||
#include "mx27.h"
|
||||
#include "mx6.h"
|
||||
#include "mx7.h"
|
||||
#include "mx7ulp.h"
|
||||
|
||||
#define imx_map_entry(soc, name, _type) { \
|
||||
.virtual = soc ## _IO_P2V(soc ## _ ## name ## _BASE_ADDR), \
|
||||
|
|
|
@ -21,6 +21,17 @@ diag_reg_offset:
|
|||
|
||||
ENTRY(v7_secondary_startup)
|
||||
ARM_BE8(setend be) @ go BE8 if entered LE
|
||||
mrc p15, 0, r0, c0, c0, 0
|
||||
ldr r1, =0xf00
|
||||
orr r1, r1, #0xff
|
||||
mov r0, r0, lsr #4
|
||||
and r0, r0, r1
|
||||
/* 0xc07 is cortex A7's ID */
|
||||
ldr r1, =0xc00
|
||||
orr r1, r1, #0x7
|
||||
cmp r0, r1
|
||||
beq secondary_startup
|
||||
|
||||
set_diag_reg
|
||||
b secondary_startup
|
||||
ENDPROC(v7_secondary_startup)
|
||||
|
|
|
@ -0,0 +1,776 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 teh 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>
|
||||
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x0
|
||||
#define PM_INFO_TTBR_OFFSET 0x4
|
||||
#define PM_INFO_MMDC_V_OFFSET 0x8
|
||||
#define PM_INFO_IOMUXC_V_OFFSET 0xc
|
||||
#define PM_INFO_CCM_V_OFFSET 0x10
|
||||
#define PM_INFO_L2_V_OFFSET 0x14
|
||||
#define PM_INFO_ANATOP_V_OFFSET 0x18
|
||||
#define PM_INFO_IO_NUM_OFFSET 0x1c
|
||||
#define PM_INFO_IO_VAL_OFFSET 0x20
|
||||
|
||||
#define MX6Q_MMDC_MAPSR 0x404
|
||||
#define MX6Q_MMDC_MPDGCTRL0 0x83c
|
||||
|
||||
.global mx6sl_lpm_wfi_start
|
||||
.global mx6sl_lpm_wfi_end
|
||||
|
||||
.macro pll_do_wait_lock
|
||||
1:
|
||||
ldr r7, [r10, r8]
|
||||
ands r7, #0x80000000
|
||||
beq 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_do_wait
|
||||
2:
|
||||
ldr r7, [r10, #0x48]
|
||||
cmp r7, #0x0
|
||||
bne 2b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
|
||||
/*
|
||||
* if in audio_bus_freq_mode, skip to
|
||||
* audio_mode low power setting.
|
||||
*/
|
||||
cmp r1, #0x1
|
||||
beq audio_mode
|
||||
/*
|
||||
* Now set DDR rate to 1MHz.
|
||||
* DDR is from bypassed PLL2 on periph2_clk2 path.
|
||||
* Set the periph2_clk2_podf to divide by 8.
|
||||
*/
|
||||
ldr r6, [r10, #0x14]
|
||||
orr r6, r6, #0x07
|
||||
str r6, [r10, #0x14]
|
||||
|
||||
/* Now set MMDC PODF to divide by 3. */
|
||||
ldr r6, [r10, #0x14]
|
||||
bic r6, r6, #0x38
|
||||
orr r6, r6, #0x10
|
||||
str r6, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
/* Set the AHB to 3MHz. AXI to 3MHz. */
|
||||
ldr r6, [r10, #0x14]
|
||||
/*r12 stores the origin AHB podf value */
|
||||
mov r12, r6
|
||||
orr r6, r6, #0x1c00
|
||||
orr r6, r6, #0x70000
|
||||
str r6, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
/* Now set ARM to 24MHz.
|
||||
* Move ARM to be sourced from step_clk
|
||||
* after setting step_clk to 24MHz.
|
||||
*/
|
||||
ldr r6, [r10, #0x0c]
|
||||
bic r6, r6, #0x100
|
||||
str r6, [r10, #0xc]
|
||||
/*Now pll1_sw_clk to step_clk */
|
||||
ldr r6, [r10, #0x0c]
|
||||
orr r6, r6, #0x4
|
||||
str r6, [r10, #0x0c]
|
||||
|
||||
/* Bypass PLL1 and power it down */
|
||||
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
|
||||
ldr r6, =(1 << 16)
|
||||
orr r6, r6, #0x1000
|
||||
str r6, [r10, #0x04]
|
||||
|
||||
/*
|
||||
* Set the ARM PODF to divide by 8.
|
||||
* IPG is at 1.5MHz here, we need ARM to
|
||||
* run at the 12:5 ratio (WAIT mode issue).
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
|
||||
ldr r11, [r10, #0x10]
|
||||
ldr r6, =0x07
|
||||
str r6, [r10, #0x10]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
b ccm_idle_done
|
||||
|
||||
audio_mode:
|
||||
/*
|
||||
* MMDC is sourced from pll2_200M.
|
||||
* Set the mmdc_podf to div by 8
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
|
||||
ldr r6, [r10, #0x14]
|
||||
orr r6, r6, #0x38
|
||||
str r6, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
/*
|
||||
* ARM is sourced from pll2_pfd2_400M here.
|
||||
* switch ARM to bypassed PLL1
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
|
||||
ldr r6, [r10, #0x0c]
|
||||
bic r6, r6, #0x4
|
||||
str r6, [r10, #0xc]
|
||||
|
||||
/*
|
||||
* set the arm_podf to divide by 3
|
||||
* as IPG is at 4MHz, we cannot run
|
||||
* arm clk above 9.6MHz when system
|
||||
* enter WAIT mode
|
||||
*/
|
||||
ldr r11, [r10, #0x10]
|
||||
ldr r6, =0x2
|
||||
str r6, [r10, #0x10]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
ccm_idle_done:
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_exit_idle
|
||||
|
||||
/*
|
||||
* If in audio_bus_freq_mode, skip to
|
||||
* audio_mode ccm restore.
|
||||
*/
|
||||
cmp r1, #0x1
|
||||
beq audio_ccm_restore
|
||||
|
||||
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
|
||||
/* Power up PLL1 and un-bypass it. */
|
||||
ldr r6, =(1 << 12)
|
||||
str r6, [r10, #0x08]
|
||||
|
||||
/* Wait for PLL1 to relock */
|
||||
ldr r8, =0x0
|
||||
pll_do_wait_lock
|
||||
|
||||
ldr r6, =(1 << 16)
|
||||
str r6, [r10, #0x08]
|
||||
|
||||
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
|
||||
/* Set PLL1_sw_clk back to PLL1 */
|
||||
ldr r6, [r10, #0x0c]
|
||||
bic r6, r6, #0x4
|
||||
str r6, [r10, #0x0c]
|
||||
|
||||
/* Restore AHB/AXI back */
|
||||
str r12, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
/* restore mmdc back to 24MHz*/
|
||||
ldr r6, [r10, #0x14]
|
||||
bic r6, r6, #0x3f
|
||||
str r6, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
b ccm_exit_done
|
||||
|
||||
audio_ccm_restore:
|
||||
/* move arm clk back to pll2_pfd2_400M */
|
||||
ldr r6, [r10, #0xc]
|
||||
orr r6, r6, #0x4
|
||||
str r6, [r10, #0xc]
|
||||
|
||||
/* restore mmdc podf */
|
||||
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
|
||||
ldr r6, [r10, #0x14]
|
||||
bic r6, r6, #0x38
|
||||
orr r6, #0x8
|
||||
str r6, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
ccm_exit_done:
|
||||
|
||||
.endm
|
||||
|
||||
.macro check_pll_state
|
||||
|
||||
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
|
||||
/*
|
||||
* Check whether any PLL is enabled, as only when
|
||||
* there is no PLLs enabled, 2p5 can be off and
|
||||
* only enable the weak one. PLL1 will be powered
|
||||
* down late, so no need to check PLL1 state.
|
||||
*/
|
||||
|
||||
/* sys PLL2 */
|
||||
ldr r6, [r10, #0x30]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 1f
|
||||
|
||||
/* usb PLL3 */
|
||||
ldr r6, [r10, #0x10]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 1f
|
||||
|
||||
/* audio PLL4 */
|
||||
ldr r6, [r10, #0x70]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 1f
|
||||
|
||||
/* video PLL5 */
|
||||
ldr r6, [r10, #0xa0]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 1f
|
||||
|
||||
/* enet PLL6 */
|
||||
ldr r6, [r10, #0xe0]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 1f
|
||||
|
||||
/* usb host PLL7 */
|
||||
ldr r6, [r10, #0x20]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 1f
|
||||
|
||||
ldr r4, =0x1
|
||||
b check_done
|
||||
1:
|
||||
ldr r4, =0x0
|
||||
|
||||
check_done:
|
||||
.endm
|
||||
|
||||
.macro anatop_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
|
||||
cmp r4, #0x0
|
||||
beq anatop_enter_done
|
||||
|
||||
/* Disable 1p1 brown out. */
|
||||
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
|
||||
ldr r6, [r10, #0x110]
|
||||
bic r6, r6, #0x2
|
||||
str r6, [r10, #0x110]
|
||||
/*
|
||||
* Set the OSC bias current to -37.5%
|
||||
* to drop the power on VDDHIGH.
|
||||
*/
|
||||
ldr r6, [r10, #0x150]
|
||||
orr r6, r6, #0xc000
|
||||
str r6, [r10, #0x150]
|
||||
|
||||
/*
|
||||
* if the usb VBUS wakeup is enabled, skip
|
||||
* disable main 2p5.
|
||||
*/
|
||||
cmp r2, #0x1
|
||||
beq anatop_enter_done
|
||||
|
||||
/* Enable the week 2p5 */
|
||||
ldr r6, [r10, #0x130]
|
||||
orr r6, r6, #0x40000
|
||||
str r6, [r10, #0x130]
|
||||
|
||||
/* Disable main 2p5. */
|
||||
ldr r6, [r10, #0x130]
|
||||
bic r6, r6, #0x1
|
||||
str r6, [r10, #0x130]
|
||||
|
||||
/*
|
||||
* Cannot diable regular bandgap
|
||||
* in LDO-enable mode. The bandgap
|
||||
* is required for ARM-LDO to regulate
|
||||
* the voltage.
|
||||
*/
|
||||
ldr r6, [r10, #0x140]
|
||||
and r6, r6, #0x1f
|
||||
cmp r6, #0x1f
|
||||
bne anatop_enter_done
|
||||
|
||||
/* Enable low power bandgap */
|
||||
ldr r6, [r10, #0x260]
|
||||
orr r6, r6, #0x20
|
||||
str r6, [r10, #0x260]
|
||||
|
||||
/*
|
||||
* Turn off the bias current
|
||||
* from the regular bandgap.
|
||||
*/
|
||||
ldr r6, [r10, #0x260]
|
||||
orr r6, r6, #0x80
|
||||
str r6, [r10, #0x260]
|
||||
|
||||
/*
|
||||
* Clear the REFTTOP+SELFBIASOFF,
|
||||
* self_bais circuit of the band gap.
|
||||
* Per RM, should be cleared when
|
||||
* band gap is powered down.
|
||||
*/
|
||||
ldr r6, [r10, #0x150]
|
||||
bic r6, r6, #0x8
|
||||
str r6, [r10, #0x150]
|
||||
|
||||
/* Power down the regular bandgap */
|
||||
ldr r6, [r10, #0x150]
|
||||
orr r6, r6, #0x1
|
||||
str r6, [r10, #0x150]
|
||||
anatop_enter_done:
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_exit_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_ANATOP_V_OFFSET]
|
||||
cmp r4, #0x0
|
||||
beq skip_anatop_restore
|
||||
|
||||
cmp r2, #0x1
|
||||
beq ldo2p5_not_disabled
|
||||
/*
|
||||
* Regular bandgap will not be disabled
|
||||
* in LDO-enabled mode as it is required
|
||||
* for ARM-LDO to reguulate the voltage.
|
||||
*/
|
||||
ldr r6, [r10, #0x140]
|
||||
and r6, r6, #0x1f
|
||||
cmp r6, #0x1f
|
||||
bne skip_bandgap_restore
|
||||
|
||||
/* Power up the regular bandgap */
|
||||
ldr r6, [r10, #0x150]
|
||||
bic r6, r6, #0x1
|
||||
str r6, [r10, #0x150]
|
||||
|
||||
/* wait for bandgap stable */
|
||||
3:
|
||||
ldr r6, [r10, #0x150]
|
||||
and r6, r6, #0x80
|
||||
cmp r6, #0x80
|
||||
bne 3b
|
||||
|
||||
/* now disable bandgap self-bias circuit */
|
||||
ldr r6, [r10, #0x150]
|
||||
orr r6, r6, #0x8
|
||||
str r6, [r10, #0x150]
|
||||
|
||||
/* Turn on the bias current
|
||||
* from the regular bandgap.
|
||||
*/
|
||||
ldr r6, [r10, #0x260]
|
||||
bic r6, r6, #0x80
|
||||
str r6, [r10, #0x260]
|
||||
|
||||
/* Disable the low power bandgap */
|
||||
ldr r6, [r10, #0x260]
|
||||
bic r6, r6, #0x20
|
||||
str r6, [r10, #0x260]
|
||||
|
||||
skip_bandgap_restore:
|
||||
/* Enable main 2p5. */
|
||||
ldr r6, [r10, #0x130]
|
||||
orr r6, r6, #0x1
|
||||
str r6, [r10, #0x130]
|
||||
|
||||
/* Ensure the 2p5 is up */
|
||||
5:
|
||||
ldr r6, [r10, #0x130]
|
||||
and r6, r6, #0x20000
|
||||
cmp r6, #0x20000
|
||||
bne 5b
|
||||
|
||||
/* Disable the weak 2p5 */
|
||||
ldr r6, [r10, #0x130]
|
||||
bic r6, r6, #0x40000
|
||||
str r6, [r10, #0x130]
|
||||
|
||||
ldo2p5_not_disabled:
|
||||
/*
|
||||
* Set the OSC bias current to max
|
||||
* value for normal operation.
|
||||
*/
|
||||
ldr r6, [r10, #0x150]
|
||||
bic r6, r6, #0xc000
|
||||
str r6, [r10, #0x150]
|
||||
|
||||
/* Enable 1p1 brown out, */
|
||||
ldr r6, [r10, #0x110]
|
||||
orr r6, r6, #0x2
|
||||
str r6, [r10, #0x110]
|
||||
|
||||
skip_anatop_restore:
|
||||
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_enter_dvfs_mode
|
||||
|
||||
/* disable automatic power saving. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
/* disable power down timer */
|
||||
ldr r7, [r10, #0x04]
|
||||
bic r7, r7, #0xff00
|
||||
str r7, [r10, #0x04]
|
||||
|
||||
/* Make the DDR explicitly enter self-refresh. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
poll_dvfs_set:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
beq poll_dvfs_set
|
||||
|
||||
/* set SBS step-by step mode */
|
||||
ldr r7, [r10, #0x410]
|
||||
orr r7, r7, #0x100
|
||||
str r7, [r10, #0x410]
|
||||
|
||||
.endm
|
||||
|
||||
.macro resume_mmdc
|
||||
/* restore MMDC IO */
|
||||
ldr r10, [r0, #PM_INFO_IOMUXC_V_OFFSET]
|
||||
|
||||
ldr r6, [r0, #PM_INFO_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
6:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 6b
|
||||
|
||||
/*
|
||||
* Need to reset the FIFO to avoid MMDC lockup
|
||||
* caused because of floating/changing the
|
||||
* configuration of many DDR IO pads.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MMDC_V_OFFSET]
|
||||
/* reset read FIFO, RST_RD_FIFO */
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
7:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 7b
|
||||
|
||||
/* reset FIFO a second time */
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
8:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 <<31)
|
||||
bne 8b
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MMDC_V_OFFSET]
|
||||
/* Let DDR out of self-refresh */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
9:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
bne 9b
|
||||
|
||||
/* enable power down timer */
|
||||
ldr r7, [r10, #0x04]
|
||||
orr r7, r7, #0x5500
|
||||
str r7, [r10, #0x04]
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
/* Clear SBS - unblock DDR accesses */
|
||||
ldr r7, [r10, #0x410]
|
||||
bic r7, r7, #0x100
|
||||
str r7, [r10, #0x410]
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_set_to_ocram
|
||||
|
||||
/* save ttbr */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_TTBR_OFFSET]
|
||||
|
||||
/*
|
||||
* 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 the IRAM page table.
|
||||
* 3. Disable page table walks in TTBR0 (PD0 = 1)
|
||||
* 4. Set TTBR0.N=1, implying 0-2G is transslated by TTBR0
|
||||
* and 2-4G is translated by TTBR1.
|
||||
*/
|
||||
|
||||
ldr r6, =iram_tlb_phys_addr
|
||||
ldr r7, [r6]
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the BTAC. */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
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
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_back_to_ddr
|
||||
|
||||
/* 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
|
||||
/* 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
|
||||
/* Restore ttbr */
|
||||
ldr r7, [r0, #PM_INFO_TTBR_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.extern iram_tlb_phys_addr
|
||||
|
||||
/*
|
||||
* imx6sl_low_power_wfi code
|
||||
* r0: wfi code base address
|
||||
* r1: audio_bus_freq mode stat
|
||||
* r2: vbus_ldo status
|
||||
* r4: used for store the PLLs state
|
||||
* r11: used for saving the ARM_PODF origin value
|
||||
* r12: used for saving AHB_PODF origin value
|
||||
*/
|
||||
.align 3
|
||||
ENTRY(imx6sl_low_power_idle)
|
||||
|
||||
mx6sl_lpm_wfi_start:
|
||||
push {r4-r12}
|
||||
|
||||
tlb_set_to_ocram
|
||||
disable_l1_dcache
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* sync L2 */
|
||||
ldr r10, [r0, #PM_INFO_L2_V_OFFSET]
|
||||
/* Wait for background operations to complete. */
|
||||
wait_for_l2_idle:
|
||||
ldr r6, [r10, #0x730]
|
||||
cmp r6, #0x0
|
||||
bne wait_for_l2_idle
|
||||
|
||||
mov r6, #0x0
|
||||
str r6, [r10, #0x730]
|
||||
/* disable L2 */
|
||||
str r6, [r10, #0x100]
|
||||
|
||||
dsb
|
||||
isb
|
||||
#endif
|
||||
|
||||
/* make sure MMDC in self-refresh */
|
||||
ldr r10, [r0, #PM_INFO_MMDC_V_OFFSET]
|
||||
mmdc_enter_dvfs_mode
|
||||
/* save DDR IO settings and set to LPM mode*/
|
||||
ldr r10, [r0, #PM_INFO_IOMUXC_V_OFFSET]
|
||||
ldr r6, =0x0
|
||||
ldr r7, [r0, #PM_INFO_IO_NUM_OFFSET]
|
||||
ldr r8, =PM_INFO_IO_VAL_OFFSET
|
||||
add r8, r8, r0
|
||||
|
||||
/* imx6sl's last 3 IOs need special setting */
|
||||
sub r7, r7, #0x3
|
||||
save_and_set_mmdc_io_lpm:
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r6, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
subs r7, r7, #0x1
|
||||
bne save_and_set_mmdc_io_lpm
|
||||
ldr r6, =0x1000
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
str r6, [r10, r9]
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r6, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
ldr r6, =0x80000
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r6, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
|
||||
|
||||
/* check the PLLs lock state */
|
||||
check_pll_state
|
||||
|
||||
ccm_enter_idle
|
||||
/* if in audio low power mode, no
|
||||
* need to do anatop setting.
|
||||
*/
|
||||
cmp r1, #0x1
|
||||
beq do_wfi
|
||||
anatop_enter_idle
|
||||
do_wfi:
|
||||
wfi
|
||||
/*
|
||||
* Add these nops so that the
|
||||
* prefetcher will not try to get
|
||||
* any instrutions from DDR.
|
||||
* The prefetch depth is about 23
|
||||
* on A9, so adding 25 nops.
|
||||
*/
|
||||
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 the ARM PODF first to speed
|
||||
* up the restore procedure
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_CCM_V_OFFSET]
|
||||
/* Restore arm_clk_podf */
|
||||
str r11, [r10, #0x10]
|
||||
ccm_do_wait
|
||||
|
||||
/*
|
||||
* if in audio low power mode, skip
|
||||
* restore the anatop setting.
|
||||
*/
|
||||
cmp r1, #0x1
|
||||
beq skip_analog_restore
|
||||
anatop_exit_idle
|
||||
|
||||
skip_analog_restore:
|
||||
ccm_exit_idle
|
||||
resume_mmdc
|
||||
|
||||
/* enable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
orr r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
ldr r10, [r0, #PM_INFO_L2_V_OFFSET]
|
||||
mov r7, #0x1
|
||||
/* enable L2 */
|
||||
str r7, [r10, #0x100]
|
||||
#endif
|
||||
tlb_back_to_ddr
|
||||
|
||||
/* Restore register */
|
||||
pop {r4 - r12}
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* Add ltorg here to ensure that all
|
||||
* literals are stored here and are
|
||||
* within the text space.
|
||||
*/
|
||||
.ltorg
|
||||
mx6sl_lpm_wfi_end:
|
|
@ -0,0 +1,780 @@
|
|||
/*
|
||||
* Copyright (C) 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>
|
||||
|
||||
#define PM_INFO_PBASE_OFFSET 0x0
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x4
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x8
|
||||
#define PM_INFO_PM_INFO_TTBR_OFFSET 0xc
|
||||
#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
|
||||
#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
|
||||
#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x18
|
||||
#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x1c
|
||||
#define PM_INFO_MX6Q_CCM_P_OFFSET 0x20
|
||||
#define PM_INFO_MX6Q_CCM_V_OFFSET 0x24
|
||||
#define PM_INFO_MX6Q_GPC_P_OFFSET 0x28
|
||||
#define PM_INFO_MX6Q_GPC_V_OFFSET 0x2c
|
||||
#define PM_INFO_MX6Q_ANATOP_P_OFFSET 0x30
|
||||
#define PM_INFO_MX6Q_ANATOP_V_OFFSET 0x34
|
||||
#define PM_INFO_MX6Q_SRC_P_OFFSET 0x38
|
||||
#define PM_INFO_MX6Q_SRC_V_OFFSET 0x3c
|
||||
#define PM_INFO_MX6Q_L2_P_OFFSET 0x40
|
||||
#define PM_INFO_MX6Q_L2_V_OFFSET 0x44
|
||||
#define PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET 0x48
|
||||
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x4c
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x50
|
||||
|
||||
#define MX6Q_MMDC_MAPSR 0x404
|
||||
#define MX6Q_MMDC_MPDGCTRL0 0x83c
|
||||
#define MX6Q_SRC_GPR1 0x20
|
||||
#define MX6Q_SRC_GPR2 0x24
|
||||
#define MX6Q_GPC_IMR1 0x08
|
||||
#define MX6Q_GPC_IMR2 0x0c
|
||||
#define MX6Q_GPC_IMR3 0x10
|
||||
#define MX6Q_GPC_IMR4 0x14
|
||||
#define MX6Q_CCM_CCR 0x0
|
||||
|
||||
.globl mx6sll_lpm_wfi_start
|
||||
.globl mx6sll_lpm_wfi_end
|
||||
|
||||
.macro pll_do_wait_lock
|
||||
1:
|
||||
ldr r7, [r10, r8]
|
||||
ands r7, #0x80000000
|
||||
beq 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_do_wait
|
||||
2:
|
||||
ldr r7, [r10, #0x48]
|
||||
cmp r7, #0x0
|
||||
bne 2b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
|
||||
/* set ahb to 3MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set perclk to 6MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
orr r7, r7, #0x3
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc to 1MHz, periph2_clk2 need to be @8MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x2
|
||||
orr r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/*
|
||||
* disable pll2, suppose when system enter low
|
||||
* power idle mode, only 396MHz pfd needs pll2,
|
||||
* now we switch arm clock to OSC, we can disable
|
||||
* pll2 now, gate pll2_pfd2 first.
|
||||
*/
|
||||
ldr r7, [r10, #0x100]
|
||||
orr r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
ldr r7, [r10, #0x30]
|
||||
orr r7, r7, #0x1000
|
||||
bic r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* enable pll2 and pll2_pfd2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
bic r7, r7, #0x1000
|
||||
orr r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
ldr r8, =0x30
|
||||
pll_do_wait_lock
|
||||
|
||||
ldr r7, [r10, #0x100]
|
||||
bic r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_CCM_P_OFFSET]
|
||||
|
||||
/* set perclk back to 24MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x7
|
||||
bic r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set ahb div back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/*
|
||||
* check whether any PLL is enabled, as only when
|
||||
* there is no PLLs enabled, 2P5 and 1P1 can be
|
||||
* off and only enable weak ones.
|
||||
*/
|
||||
|
||||
/* arm pll1 */
|
||||
ldr r7, [r10, #0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* sys pll2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb pll3 */
|
||||
ldr r7, [r10, #0x10]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* audio pll4 */
|
||||
ldr r7, [r10, #0x70]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* vidio pll5 */
|
||||
ldr r7, [r10, #0xa0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enet pll6 */
|
||||
ldr r7, [r10, #0xe0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb host pll7 */
|
||||
ldr r7, [r10, #0x20]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enable weak 2P5 and turn off regular 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable weak 1p1 and turn off regular 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
|
||||
/* low power band gap enable */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/*
|
||||
* clear the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
* Per RM, should be cleared when
|
||||
* band gap is powered down.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn off regular bandgap */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
10:
|
||||
/* switch to RC-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x40000000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* lower OSC current by 37.5% */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* disconnect vdd_high_in and vdd_snvs_in */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x1000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* increase OSC current to normal */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn on XTAL-OSC and detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x40000000
|
||||
orr r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* wait for XTAL stable */
|
||||
14:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, r7, #0x8000
|
||||
beq 14b
|
||||
|
||||
/* switch to XTAL-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
15:
|
||||
/* check whether we need to enable 2P5/1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x40000
|
||||
beq 11f
|
||||
|
||||
/* turn on regular bandgap and wait for stable */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
13:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, #0x80
|
||||
beq 13b
|
||||
|
||||
/*
|
||||
* set the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn on the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* low power band gap disable */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
12:
|
||||
/* enable regular 2P5 and turn off weak 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* Ensure the 2P5 is up. */
|
||||
3:
|
||||
ldr r7, [r10, #0x130]
|
||||
ands r7, r7, #0x20000
|
||||
beq 3b
|
||||
ldr r7, [r10, #0x130]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable regular 1p1 and turn off weak 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
4:
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x20000
|
||||
beq 4b
|
||||
ldr r7, [r10, #0x110]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
11:
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_enter_dvfs_mode
|
||||
|
||||
/* disable automatic power savings. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
/* make the DDR explicitly enter self-refresh. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
5:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
beq 5b
|
||||
|
||||
.endm
|
||||
|
||||
.macro resume_mmdc
|
||||
|
||||
/* restore MMDC IO */
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
|
||||
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
6:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 6b
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
|
||||
|
||||
/* reset read FIFO, RST_RD_FIFO */
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
7:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 7b
|
||||
|
||||
/* reset FIFO a second time */
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
8:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 8b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
9:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
bne 9b
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_set_to_ocram
|
||||
|
||||
/* save ttbr */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_back_to_ddr
|
||||
|
||||
/* 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
|
||||
|
||||
/* restore ttbr */
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.extern iram_tlb_phys_addr
|
||||
|
||||
/* imx6sx_low_power_idle */
|
||||
|
||||
.align 3
|
||||
ENTRY(imx6sll_low_power_idle)
|
||||
mx6sll_lpm_wfi_start:
|
||||
push {r4 - r10}
|
||||
|
||||
/* get necessary info from pm_info */
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r5, =imx6sll_low_power_idle
|
||||
ldr r6, =wakeup
|
||||
sub r6, r6, r5
|
||||
add r8, r1, r2
|
||||
add r3, r8, r6
|
||||
|
||||
/* store physical resume addr and pm_info address. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
|
||||
str r3, [r10, #0x20]
|
||||
str r1, [r10, #0x24]
|
||||
|
||||
/* save disagnostic register */
|
||||
mrc p15, 0, r7, c15, c0, 1
|
||||
str r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET]
|
||||
|
||||
/* set ARM power to be gated */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x1
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* sync L2 */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
|
||||
|
||||
/* Wait for background operations to complete. */
|
||||
wait_for_l2_to_idle:
|
||||
ldr r7, [r10, #0x730]
|
||||
cmp r7, #0x0
|
||||
bne wait_for_l2_to_idle
|
||||
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #0x730]
|
||||
/* disable L2 */
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
dsb
|
||||
isb
|
||||
#endif
|
||||
|
||||
tlb_set_to_ocram
|
||||
|
||||
/* make sure MMDC in self-refresh */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
mmdc_enter_dvfs_mode
|
||||
|
||||
/* save DDR IO settings */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldr r6, =0x0
|
||||
ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r8, r8, r0
|
||||
save_and_set_mmdc_io_lpm:
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r6, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
subs r7, r7, #0x1
|
||||
bne save_and_set_mmdc_io_lpm
|
||||
|
||||
mov r5, #0x0
|
||||
ccm_enter_idle
|
||||
anatop_enter_idle
|
||||
|
||||
/*
|
||||
* mask all GPC interrupts before
|
||||
* enabling the RBC counters to
|
||||
* avoid the counter starting too
|
||||
* early if an interupt is already
|
||||
* pending.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r4, [r10, #MX6Q_GPC_IMR1]
|
||||
ldr r5, [r10, #MX6Q_GPC_IMR2]
|
||||
ldr r6, [r10, #MX6Q_GPC_IMR3]
|
||||
ldr r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
ldr r3, =0xffffffff
|
||||
str r3, [r10, #MX6Q_GPC_IMR1]
|
||||
str r3, [r10, #MX6Q_GPC_IMR2]
|
||||
str r3, [r10, #MX6Q_GPC_IMR3]
|
||||
str r3, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* enable the RBC bypass counter here
|
||||
* to hold off the interrupts. RBC counter
|
||||
* = 4 (120us). With this setting, the latency
|
||||
* from wakeup interrupt to ARM power up
|
||||
* is ~130uS.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
bic r3, r3, #(0x3f << 21)
|
||||
orr r3, r3, #(0x4 << 21)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* enable the counter. */
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
orr r3, r3, #(0x1 << 27)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* unmask all the GPC interrupts. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
str r4, [r10, #MX6Q_GPC_IMR1]
|
||||
str r5, [r10, #MX6Q_GPC_IMR2]
|
||||
str r6, [r10, #MX6Q_GPC_IMR3]
|
||||
str r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* now delay for a short while (3usec)
|
||||
* ARM is at 24MHz at this point
|
||||
* so a short loop should be enough.
|
||||
* this delay is required to ensure that
|
||||
* the RBC counter can start counting in
|
||||
* case an interrupt is already pending
|
||||
* or in case an interrupt arrives just
|
||||
* as ARM is about to assert DSM_request.
|
||||
*/
|
||||
ldr r4, =50
|
||||
rbc_loop:
|
||||
subs r4, r4, #0x1
|
||||
bne rbc_loop
|
||||
|
||||
wfi
|
||||
|
||||
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
|
||||
|
||||
mov r5, #0x0
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
resume_mmdc
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
/* enable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
orr r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
|
||||
mov r7, #0x1
|
||||
/* enable L2 */
|
||||
str r7, [r10, #0x100]
|
||||
#endif
|
||||
|
||||
tlb_back_to_ddr
|
||||
|
||||
/* Restore registers */
|
||||
pop {r4 - r10}
|
||||
mov pc, lr
|
||||
|
||||
wakeup:
|
||||
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r1, #0x0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r1, #0x1800
|
||||
mcr p15, 0, r1, c1, c0, 0
|
||||
isb
|
||||
/* restore disagnostic register */
|
||||
ldr r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET]
|
||||
mcr p15, 0, r7, c15, c0, 1
|
||||
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
/* clear core0's entry and parameter */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #MX6Q_SRC_GPR1]
|
||||
str r7, [r10, #MX6Q_SRC_GPR2]
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
mov r5, #0x1
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
resume_mmdc
|
||||
|
||||
/* Restore registers */
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
mx6sll_lpm_wfi_end:
|
|
@ -0,0 +1,887 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 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>
|
||||
|
||||
#define PM_INFO_PBASE_OFFSET 0x0
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x4
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x8
|
||||
#define PM_INFO_PM_INFO_TTBR_OFFSET 0xc
|
||||
#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
|
||||
#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
|
||||
#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x18
|
||||
#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x1c
|
||||
#define PM_INFO_MX6Q_CCM_P_OFFSET 0x20
|
||||
#define PM_INFO_MX6Q_CCM_V_OFFSET 0x24
|
||||
#define PM_INFO_MX6Q_GPC_P_OFFSET 0x28
|
||||
#define PM_INFO_MX6Q_GPC_V_OFFSET 0x2c
|
||||
#define PM_INFO_MX6Q_L2_P_OFFSET 0x30
|
||||
#define PM_INFO_MX6Q_L2_V_OFFSET 0x34
|
||||
#define PM_INFO_MX6Q_ANATOP_P_OFFSET 0x38
|
||||
#define PM_INFO_MX6Q_ANATOP_V_OFFSET 0x3c
|
||||
#define PM_INFO_MX6Q_SRC_P_OFFSET 0x40
|
||||
#define PM_INFO_MX6Q_SRC_V_OFFSET 0x44
|
||||
#define PM_INFO_MX6Q_SEMA4_P_OFFSET 0x48
|
||||
#define PM_INFO_MX6Q_SEMA4_V_OFFSET 0x4c
|
||||
#define PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET 0x50
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x54
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x58
|
||||
|
||||
#define MX6Q_MMDC_MAPSR 0x404
|
||||
#define MX6Q_MMDC_MPDGCTRL0 0x83c
|
||||
#define MX6Q_SRC_GPR1 0x20
|
||||
#define MX6Q_SRC_GPR2 0x24
|
||||
#define MX6Q_GPC_IMR1 0x08
|
||||
#define MX6Q_GPC_IMR2 0x0c
|
||||
#define MX6Q_GPC_IMR3 0x10
|
||||
#define MX6Q_GPC_IMR4 0x14
|
||||
#define MX6Q_CCM_CCR 0x0
|
||||
|
||||
.globl mx6sx_lpm_wfi_start
|
||||
.globl mx6sx_lpm_wfi_end
|
||||
|
||||
.macro pll_do_wait_lock
|
||||
1:
|
||||
ldr r7, [r10, r8]
|
||||
ands r7, #0x80000000
|
||||
beq 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_do_wait
|
||||
2:
|
||||
ldr r7, [r10, #0x48]
|
||||
cmp r7, #0x0
|
||||
bne 2b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
|
||||
/* set ahb to 3MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set perclk to 6MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
orr r7, r7, #0x3
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc to 1MHz, periph2_clk2 need to be @8MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x2
|
||||
orr r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
|
||||
/* set pll1_sw to from pll1 main */
|
||||
ldr r7, [r10, #0xc]
|
||||
bic r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set step from osc */
|
||||
ldr r7, [r10, #0xc]
|
||||
bic r7, r7, #0x100
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set pll1_sw to from step */
|
||||
ldr r7, [r10, #0xc]
|
||||
orr r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/* Disable PLL1 bypass output */
|
||||
ldr r7, [r10]
|
||||
bic r7, r7, #0x12000
|
||||
str r7, [r10]
|
||||
|
||||
/*
|
||||
* disable pll2, suppose when system enter low
|
||||
* power idle mode, only 396MHz pfd needs pll2,
|
||||
* now we switch arm clock to OSC, we can disable
|
||||
* pll2 now, gate pll2_pfd2 first.
|
||||
*/
|
||||
ldr r7, [r10, #0x100]
|
||||
orr r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
ldr r7, [r10, #0x30]
|
||||
orr r7, r7, #0x1000
|
||||
bic r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* enable pll2 and pll2_pfd2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
bic r7, r7, #0x1000
|
||||
orr r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
ldr r8, =0x30
|
||||
pll_do_wait_lock
|
||||
|
||||
ldr r7, [r10, #0x100]
|
||||
bic r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
/* enable PLL1 bypass output */
|
||||
ldr r7, [r10]
|
||||
orr r7, r7, #0x12000
|
||||
str r7, [r10]
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_CCM_P_OFFSET]
|
||||
|
||||
/* set perclk back to 24MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x7
|
||||
bic r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set ahb div back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
/* set pll1_sw to from pll1 main */
|
||||
ldr r7, [r10, #0xc]
|
||||
bic r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set step from pll2_pfd2 */
|
||||
ldr r7, [r10, #0xc]
|
||||
orr r7, r7, #0x100
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set pll1_sw to from step */
|
||||
ldr r7, [r10, #0xc]
|
||||
orr r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/*
|
||||
* check whether any PLL is enabled, as only when
|
||||
* there is no PLLs enabled, 2P5 and 1P1 can be
|
||||
* off and only enable weak ones.
|
||||
*/
|
||||
|
||||
/* arm pll1 */
|
||||
ldr r7, [r10, #0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* sys pll2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb pll3 */
|
||||
ldr r7, [r10, #0x10]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* audio pll4 */
|
||||
ldr r7, [r10, #0x70]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* vidio pll5 */
|
||||
ldr r7, [r10, #0xa0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enet pll6 */
|
||||
ldr r7, [r10, #0xe0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb host pll7 */
|
||||
ldr r7, [r10, #0x20]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enable weak 2P5 and turn off regular 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable weak 1p1 and turn off regular 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
|
||||
/* check whether ARM LDO is bypassed */
|
||||
ldr r7, [r10, #0x140]
|
||||
and r7, r7, #0x1f
|
||||
cmp r7, #0x1f
|
||||
bne 10f
|
||||
|
||||
/* low power band gap enable */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/*
|
||||
* clear the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
* Per RM, should be cleared when
|
||||
* band gap is powered down.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn off regular bandgap */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* only switch to RC-OSC clk after TO1.2 */
|
||||
ldr r7, [r10, #0x260]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x2
|
||||
blt 10f
|
||||
|
||||
/* switch to RC-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x40000000
|
||||
str r7, [r10, #0x150]
|
||||
10:
|
||||
/* lower OSC current by 37.5% */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* increase OSC current to normal */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* only switch to RC-OSC after TO1.2 */
|
||||
ldr r7, [r10, #0x260]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x2
|
||||
blt 15f
|
||||
|
||||
/* turn on XTAL-OSC and detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x40000000
|
||||
orr r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* wait for XTAL stable */
|
||||
14:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, r7, #0x8000
|
||||
beq 14b
|
||||
|
||||
/* switch to XTAL-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
15:
|
||||
/* check whether we need to enable 2P5/1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x40000
|
||||
beq 11f
|
||||
|
||||
/* check whether ARM LDO is bypassed */
|
||||
ldr r7, [r10, #0x140]
|
||||
and r7, r7, #0x1f
|
||||
cmp r7, #0x1f
|
||||
bne 12f
|
||||
|
||||
/* turn on regular bandgap and wait for stable */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
13:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, #0x80
|
||||
beq 13b
|
||||
|
||||
/*
|
||||
* set the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn on the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* low power band gap disable */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
12:
|
||||
/* enable regular 2P5 and turn off weak 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* Ensure the 2P5 is up. */
|
||||
3:
|
||||
ldr r7, [r10, #0x130]
|
||||
ands r7, r7, #0x20000
|
||||
beq 3b
|
||||
ldr r7, [r10, #0x130]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable regular 1p1 and turn off weak 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
4:
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x20000
|
||||
beq 4b
|
||||
ldr r7, [r10, #0x110]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
11:
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_enter_dvfs_mode
|
||||
|
||||
/* disable automatic power savings. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
/* disable power down timer */
|
||||
ldr r7, [r10, #0x4]
|
||||
bic r7, r7, #0xff00
|
||||
str r7, [r10, #0x4]
|
||||
|
||||
/* make the DDR explicitly enter self-refresh. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
5:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
beq 5b
|
||||
|
||||
.endm
|
||||
|
||||
.macro resume_mmdc
|
||||
|
||||
/* restore MMDC IO */
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
|
||||
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
6:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 6b
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
|
||||
|
||||
/* reset read FIFO, RST_RD_FIFO */
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
7:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 7b
|
||||
|
||||
/* reset FIFO a second time */
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
8:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 8b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
9:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
bne 9b
|
||||
|
||||
/* enable power down timer */
|
||||
ldr r7, [r10, #0x4]
|
||||
orr r7, r7, #0x5500
|
||||
str r7, [r10, #0x4]
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
.endm
|
||||
|
||||
.macro sema4_lock
|
||||
|
||||
/* lock share memory sema4 */
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_SEMA4_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_SEMA4_P_OFFSET]
|
||||
ldrb r6, =0x1
|
||||
16:
|
||||
ldrb r7, [r10, #0x6]
|
||||
cmp r7, #0x0
|
||||
bne 16b
|
||||
strb r6, [r10, #0x6]
|
||||
|
||||
.endm
|
||||
|
||||
.macro sema4_unlock
|
||||
|
||||
/* unlock share memory sema4 */
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_SEMA4_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_SEMA4_P_OFFSET]
|
||||
ldrb r6, =0x0
|
||||
strb r6, [r10, #0x6]
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_set_to_ocram
|
||||
|
||||
/* save ttbr */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_back_to_ddr
|
||||
|
||||
/* 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
|
||||
|
||||
/* restore ttbr */
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.extern iram_tlb_phys_addr
|
||||
|
||||
/* imx6sx_low_power_idle */
|
||||
|
||||
.align 3
|
||||
ENTRY(imx6sx_low_power_idle)
|
||||
mx6sx_lpm_wfi_start:
|
||||
push {r4 - r10}
|
||||
|
||||
/* get necessary info from pm_info */
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r5, =imx6sx_low_power_idle
|
||||
ldr r6, =wakeup
|
||||
sub r6, r6, r5
|
||||
add r8, r1, r2
|
||||
add r3, r8, r6
|
||||
|
||||
/* store physical resume addr and pm_info address. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
|
||||
str r3, [r10, #0x20]
|
||||
str r1, [r10, #0x24]
|
||||
|
||||
/* save disagnostic register */
|
||||
mrc p15, 0, r7, c15, c0, 1
|
||||
str r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET]
|
||||
|
||||
/* set ARM power to be gated */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x1
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* sync L2 */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
|
||||
|
||||
/* Wait for background operations to complete. */
|
||||
wait_for_l2_to_idle:
|
||||
ldr r7, [r10, #0x730]
|
||||
cmp r7, #0x0
|
||||
bne wait_for_l2_to_idle
|
||||
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #0x730]
|
||||
/* disable L2 */
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
dsb
|
||||
isb
|
||||
#endif
|
||||
|
||||
tlb_set_to_ocram
|
||||
|
||||
/* make sure MMDC in self-refresh */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
mmdc_enter_dvfs_mode
|
||||
|
||||
/* save DDR IO settings */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldr r6, =0x0
|
||||
ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r8, r8, r0
|
||||
save_and_set_mmdc_io_lpm:
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r6, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
subs r7, r7, #0x1
|
||||
bne save_and_set_mmdc_io_lpm
|
||||
|
||||
mov r5, #0x0
|
||||
sema4_lock
|
||||
ccm_enter_idle
|
||||
anatop_enter_idle
|
||||
sema4_unlock
|
||||
|
||||
/*
|
||||
* mask all GPC interrupts before
|
||||
* enabling the RBC counters to
|
||||
* avoid the counter starting too
|
||||
* early if an interupt is already
|
||||
* pending.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r4, [r10, #MX6Q_GPC_IMR1]
|
||||
ldr r5, [r10, #MX6Q_GPC_IMR2]
|
||||
ldr r6, [r10, #MX6Q_GPC_IMR3]
|
||||
ldr r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
ldr r3, =0xffffffff
|
||||
str r3, [r10, #MX6Q_GPC_IMR1]
|
||||
str r3, [r10, #MX6Q_GPC_IMR2]
|
||||
str r3, [r10, #MX6Q_GPC_IMR3]
|
||||
str r3, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* enable the RBC bypass counter here
|
||||
* to hold off the interrupts. RBC counter
|
||||
* = 4 (120us). With this setting, the latency
|
||||
* from wakeup interrupt to ARM power up
|
||||
* is ~130uS.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
bic r3, r3, #(0x3f << 21)
|
||||
orr r3, r3, #(0x4 << 21)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* enable the counter. */
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
orr r3, r3, #(0x1 << 27)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* unmask all the GPC interrupts. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
str r4, [r10, #MX6Q_GPC_IMR1]
|
||||
str r5, [r10, #MX6Q_GPC_IMR2]
|
||||
str r6, [r10, #MX6Q_GPC_IMR3]
|
||||
str r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* now delay for a short while (3usec)
|
||||
* ARM is at 24MHz at this point
|
||||
* so a short loop should be enough.
|
||||
* this delay is required to ensure that
|
||||
* the RBC counter can start counting in
|
||||
* case an interrupt is already pending
|
||||
* or in case an interrupt arrives just
|
||||
* as ARM is about to assert DSM_request.
|
||||
*/
|
||||
ldr r4, =50
|
||||
rbc_loop:
|
||||
subs r4, r4, #0x1
|
||||
bne rbc_loop
|
||||
|
||||
wfi
|
||||
|
||||
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
|
||||
|
||||
mov r5, #0x0
|
||||
sema4_lock
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
sema4_unlock
|
||||
resume_mmdc
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
/* enable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
orr r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
|
||||
mov r7, #0x1
|
||||
/* enable L2 */
|
||||
str r7, [r10, #0x100]
|
||||
#endif
|
||||
|
||||
tlb_back_to_ddr
|
||||
|
||||
/* Restore registers */
|
||||
pop {r4 - r10}
|
||||
mov pc, lr
|
||||
|
||||
wakeup:
|
||||
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r1, #0x0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r1, #0x1800
|
||||
mcr p15, 0, r1, c1, c0, 0
|
||||
isb
|
||||
/* restore disagnostic register */
|
||||
ldr r7, [r0, #PM_INFO_MX6Q_SAVED_DIAGNOSTIC_OFFSET]
|
||||
mcr p15, 0, r7, c15, c0, 1
|
||||
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
/* clear core0's entry and parameter */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #MX6Q_SRC_GPR1]
|
||||
str r7, [r10, #MX6Q_SRC_GPR2]
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
mov r5, #0x1
|
||||
sema4_lock
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
sema4_unlock
|
||||
resume_mmdc
|
||||
|
||||
/* Restore registers */
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
mx6sx_lpm_wfi_end:
|
|
@ -0,0 +1,821 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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>
|
||||
|
||||
#define PM_INFO_PBASE_OFFSET 0x0
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x4
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x8
|
||||
#define PM_INFO_PM_INFO_TTBR_OFFSET 0xc
|
||||
#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
|
||||
#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
|
||||
#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x18
|
||||
#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x1c
|
||||
#define PM_INFO_MX6Q_CCM_P_OFFSET 0x20
|
||||
#define PM_INFO_MX6Q_CCM_V_OFFSET 0x24
|
||||
#define PM_INFO_MX6Q_GPC_P_OFFSET 0x28
|
||||
#define PM_INFO_MX6Q_GPC_V_OFFSET 0x2c
|
||||
#define PM_INFO_MX6Q_ANATOP_P_OFFSET 0x30
|
||||
#define PM_INFO_MX6Q_ANATOP_V_OFFSET 0x34
|
||||
#define PM_INFO_MX6Q_SRC_P_OFFSET 0x38
|
||||
#define PM_INFO_MX6Q_SRC_V_OFFSET 0x3c
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44
|
||||
|
||||
#define MX6Q_MMDC_MAPSR 0x404
|
||||
#define MX6Q_MMDC_MPDGCTRL0 0x83c
|
||||
#define MX6Q_SRC_GPR1 0x20
|
||||
#define MX6Q_SRC_GPR2 0x24
|
||||
#define MX6Q_GPC_IMR1 0x08
|
||||
#define MX6Q_GPC_IMR2 0x0c
|
||||
#define MX6Q_GPC_IMR3 0x10
|
||||
#define MX6Q_GPC_IMR4 0x14
|
||||
#define MX6Q_CCM_CCR 0x0
|
||||
|
||||
.globl mx6ul_lpm_wfi_start
|
||||
.globl mx6ul_lpm_wfi_end
|
||||
|
||||
.macro pll_do_wait_lock
|
||||
1:
|
||||
ldr r7, [r10, r8]
|
||||
ands r7, #0x80000000
|
||||
beq 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_do_wait
|
||||
2:
|
||||
ldr r7, [r10, #0x48]
|
||||
cmp r7, #0x0
|
||||
bne 2b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
|
||||
/* set ahb to 3MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set perclk to 6MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
orr r7, r7, #0x3
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc to 1MHz, periph2_clk2 need to be @8MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x2
|
||||
orr r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/* bypass PLL1 output to OSC */
|
||||
ldr r7, [r10]
|
||||
orr r7, r7, #(0x1 << 16)
|
||||
str r7, [r10]
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
|
||||
/* set pll1_sw to from pll1 main */
|
||||
ldr r7, [r10, #0xc]
|
||||
bic r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set step from osc */
|
||||
ldr r7, [r10, #0xc]
|
||||
bic r7, r7, #0x100
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set pll1_sw to from step */
|
||||
ldr r7, [r10, #0xc]
|
||||
orr r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/* Disable PLL1 bypass output */
|
||||
ldr r7, [r10]
|
||||
bic r7, r7, #0x12000
|
||||
str r7, [r10]
|
||||
|
||||
/*
|
||||
* disable pll2, suppose when system enter low
|
||||
* power idle mode, only 396MHz pfd needs pll2,
|
||||
* now we switch arm clock to OSC, we can disable
|
||||
* pll2 now, gate pll2_pfd2 first.
|
||||
*/
|
||||
ldr r7, [r10, #0x100]
|
||||
orr r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
ldr r7, [r10, #0x30]
|
||||
orr r7, r7, #0x1000
|
||||
bic r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* enable pll2 and pll2_pfd2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
bic r7, r7, #0x1000
|
||||
orr r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
ldr r8, =0x30
|
||||
pll_do_wait_lock
|
||||
|
||||
ldr r7, [r10, #0x100]
|
||||
bic r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
/* enable PLL1 bypass output */
|
||||
ldr r7, [r10]
|
||||
orr r7, r7, #0x12000
|
||||
str r7, [r10]
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_CCM_P_OFFSET]
|
||||
|
||||
/* set perclk back to 24MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x7
|
||||
bic r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set ahb div back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
/* set pll1_sw to from pll1 main */
|
||||
ldr r7, [r10, #0xc]
|
||||
bic r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set step from pll2_pfd2 */
|
||||
ldr r7, [r10, #0xc]
|
||||
orr r7, r7, #0x100
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
/* set pll1_sw to from step */
|
||||
ldr r7, [r10, #0xc]
|
||||
orr r7, r7, #0x4
|
||||
str r7, [r10, #0xc]
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* Unbypass PLL1 */
|
||||
ldr r7, [r10]
|
||||
bic r7, r7, #(0x1 << 16)
|
||||
str r7, [r10]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/*
|
||||
* check whether any PLL is enabled, as only when
|
||||
* there is no PLLs enabled, 2P5 and 1P1 can be
|
||||
* off and only enable weak ones.
|
||||
*/
|
||||
|
||||
/* arm pll1 */
|
||||
ldr r7, [r10, #0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* sys pll2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb pll3 */
|
||||
ldr r7, [r10, #0x10]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* audio pll4 */
|
||||
ldr r7, [r10, #0x70]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* vidio pll5 */
|
||||
ldr r7, [r10, #0xa0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enet pll6 */
|
||||
ldr r7, [r10, #0xe0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb host pll7 */
|
||||
ldr r7, [r10, #0x20]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enable weak 2P5 and turn off regular 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable weak 1p1 and turn off regular 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
|
||||
/* check whether ARM LDO is bypassed */
|
||||
ldr r7, [r10, #0x140]
|
||||
and r7, r7, #0x1f
|
||||
cmp r7, #0x1f
|
||||
bne 10f
|
||||
|
||||
/* low power band gap enable */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/*
|
||||
* clear the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
* Per RM, should be cleared when
|
||||
* band gap is powered down.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn off regular bandgap */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* switch to RC-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x40000000
|
||||
str r7, [r10, #0x150]
|
||||
10:
|
||||
/* lower OSC current by 37.5% */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* disconnect vdd_high_in and vdd_snvs_in */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x1000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* increase OSC current to normal */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn on XTAL-OSC and detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x40000000
|
||||
orr r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* wait for XTAL stable */
|
||||
14:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, r7, #0x8000
|
||||
beq 14b
|
||||
|
||||
/* switch to XTAL-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
15:
|
||||
/* check whether we need to enable 2P5/1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x40000
|
||||
beq 11f
|
||||
|
||||
/* check whether ARM LDO is bypassed */
|
||||
ldr r7, [r10, #0x140]
|
||||
and r7, r7, #0x1f
|
||||
cmp r7, #0x1f
|
||||
bne 12f
|
||||
|
||||
/* turn on regular bandgap and wait for stable */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
13:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, #0x80
|
||||
beq 13b
|
||||
|
||||
/*
|
||||
* set the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn on the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* low power band gap disable */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
12:
|
||||
/* enable regular 2P5 and turn off weak 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* Ensure the 2P5 is up. */
|
||||
3:
|
||||
ldr r7, [r10, #0x130]
|
||||
ands r7, r7, #0x20000
|
||||
beq 3b
|
||||
ldr r7, [r10, #0x130]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable regular 1p1 and turn off weak 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
4:
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x20000
|
||||
beq 4b
|
||||
ldr r7, [r10, #0x110]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
11:
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_enter_dvfs_mode
|
||||
|
||||
/* disable automatic power savings. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
/* disable power down timer */
|
||||
ldr r7, [r10, #0x4]
|
||||
bic r7, r7, #0xff00
|
||||
str r7, [r10, #0x4]
|
||||
|
||||
/* make the DDR explicitly enter self-refresh. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
5:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
beq 5b
|
||||
|
||||
.endm
|
||||
|
||||
.macro resume_mmdc
|
||||
|
||||
/* restore MMDC IO */
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
|
||||
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
6:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 6b
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
|
||||
|
||||
/* reset read FIFO, RST_RD_FIFO */
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
7:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 7b
|
||||
|
||||
/* reset FIFO a second time */
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
8:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 8b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
9:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
bne 9b
|
||||
|
||||
/* enable power down timer */
|
||||
ldr r7, [r10, #0x4]
|
||||
orr r7, r7, #0x5500
|
||||
str r7, [r10, #0x4]
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_set_to_ocram
|
||||
|
||||
/* save ttbr */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_back_to_ddr
|
||||
|
||||
/* 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
|
||||
|
||||
/* restore ttbr */
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.extern iram_tlb_phys_addr
|
||||
|
||||
/* imx6ul_low_power_idle */
|
||||
|
||||
.align 3
|
||||
ENTRY(imx6ul_low_power_idle)
|
||||
mx6ul_lpm_wfi_start:
|
||||
push {r4 - r10}
|
||||
|
||||
/* get necessary info from pm_info */
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r5, =imx6ul_low_power_idle
|
||||
ldr r6, =wakeup
|
||||
sub r6, r6, r5
|
||||
add r8, r1, r2
|
||||
add r3, r8, r6
|
||||
|
||||
/* store physical resume addr and pm_info address. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
|
||||
str r3, [r10, #0x20]
|
||||
str r1, [r10, #0x24]
|
||||
|
||||
/* set ARM power to be gated */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x1
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
tlb_set_to_ocram
|
||||
|
||||
/* make sure MMDC in self-refresh */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
mmdc_enter_dvfs_mode
|
||||
|
||||
/* save DDR IO settings */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldr r6, =0x0
|
||||
ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r8, r8, r0
|
||||
save_and_set_mmdc_io_lpm:
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r6, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
subs r7, r7, #0x1
|
||||
bne save_and_set_mmdc_io_lpm
|
||||
|
||||
mov r5, #0x0
|
||||
ccm_enter_idle
|
||||
anatop_enter_idle
|
||||
|
||||
/*
|
||||
* mask all GPC interrupts before
|
||||
* enabling the RBC counters to
|
||||
* avoid the counter starting too
|
||||
* early if an interupt is already
|
||||
* pending.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r4, [r10, #MX6Q_GPC_IMR1]
|
||||
ldr r5, [r10, #MX6Q_GPC_IMR2]
|
||||
ldr r6, [r10, #MX6Q_GPC_IMR3]
|
||||
ldr r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
ldr r3, =0xffffffff
|
||||
str r3, [r10, #MX6Q_GPC_IMR1]
|
||||
str r3, [r10, #MX6Q_GPC_IMR2]
|
||||
str r3, [r10, #MX6Q_GPC_IMR3]
|
||||
str r3, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* enable the RBC bypass counter here
|
||||
* to hold off the interrupts. RBC counter
|
||||
* = 4 (120us). With this setting, the latency
|
||||
* from wakeup interrupt to ARM power up
|
||||
* is ~130uS.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
bic r3, r3, #(0x3f << 21)
|
||||
orr r3, r3, #(0x4 << 21)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* enable the counter. */
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
orr r3, r3, #(0x1 << 27)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* unmask all the GPC interrupts. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
str r4, [r10, #MX6Q_GPC_IMR1]
|
||||
str r5, [r10, #MX6Q_GPC_IMR2]
|
||||
str r6, [r10, #MX6Q_GPC_IMR3]
|
||||
str r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* now delay for a short while (3usec)
|
||||
* ARM is at 24MHz at this point
|
||||
* so a short loop should be enough.
|
||||
* this delay is required to ensure that
|
||||
* the RBC counter can start counting in
|
||||
* case an interrupt is already pending
|
||||
* or in case an interrupt arrives just
|
||||
* as ARM is about to assert DSM_request.
|
||||
*/
|
||||
ldr r4, =50
|
||||
rbc_loop:
|
||||
subs r4, r4, #0x1
|
||||
bne rbc_loop
|
||||
|
||||
wfi
|
||||
|
||||
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
|
||||
|
||||
mov r5, #0x0
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
resume_mmdc
|
||||
/* enable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
orr r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
|
||||
tlb_back_to_ddr
|
||||
|
||||
/* Restore registers */
|
||||
pop {r4 - r10}
|
||||
mov pc, lr
|
||||
|
||||
wakeup:
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r1, #0x0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r1, #0x1800
|
||||
mcr p15, 0, r1, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
/* clear core0's entry and parameter */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #MX6Q_SRC_GPR1]
|
||||
str r7, [r10, #MX6Q_SRC_GPR2]
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
mov r5, #0x1
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
resume_mmdc
|
||||
|
||||
/* Restore registers */
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
mx6ul_lpm_wfi_end:
|
|
@ -0,0 +1,764 @@
|
|||
/*
|
||||
* Copyright (C) 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>
|
||||
|
||||
#define PM_INFO_PBASE_OFFSET 0x0
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x4
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x8
|
||||
#define PM_INFO_PM_INFO_TTBR_OFFSET 0xc
|
||||
#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
|
||||
#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
|
||||
#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x18
|
||||
#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x1c
|
||||
#define PM_INFO_MX6Q_CCM_P_OFFSET 0x20
|
||||
#define PM_INFO_MX6Q_CCM_V_OFFSET 0x24
|
||||
#define PM_INFO_MX6Q_GPC_P_OFFSET 0x28
|
||||
#define PM_INFO_MX6Q_GPC_V_OFFSET 0x2c
|
||||
#define PM_INFO_MX6Q_ANATOP_P_OFFSET 0x30
|
||||
#define PM_INFO_MX6Q_ANATOP_V_OFFSET 0x34
|
||||
#define PM_INFO_MX6Q_SRC_P_OFFSET 0x38
|
||||
#define PM_INFO_MX6Q_SRC_V_OFFSET 0x3c
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44
|
||||
|
||||
#define MX6Q_MMDC_MAPSR 0x404
|
||||
#define MX6Q_MMDC_MPDGCTRL0 0x83c
|
||||
#define MX6Q_SRC_GPR1 0x20
|
||||
#define MX6Q_SRC_GPR2 0x24
|
||||
#define MX6Q_GPC_IMR1 0x08
|
||||
#define MX6Q_GPC_IMR2 0x0c
|
||||
#define MX6Q_GPC_IMR3 0x10
|
||||
#define MX6Q_GPC_IMR4 0x14
|
||||
#define MX6Q_CCM_CCR 0x0
|
||||
|
||||
.globl mx6ull_lpm_wfi_start
|
||||
.globl mx6ull_lpm_wfi_end
|
||||
|
||||
.macro pll_do_wait_lock
|
||||
1:
|
||||
ldr r7, [r10, r8]
|
||||
ands r7, #0x80000000
|
||||
beq 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_do_wait
|
||||
2:
|
||||
ldr r7, [r10, #0x48]
|
||||
cmp r7, #0x0
|
||||
bne 2b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
|
||||
/* set ahb to 3MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set perclk to 6MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
orr r7, r7, #0x3
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc to 1MHz, periph2_clk2 need to be @8MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
orr r7, r7, #0x2
|
||||
orr r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/*
|
||||
* disable pll2, suppose when system enter low
|
||||
* power idle mode, only 396MHz pfd needs pll2,
|
||||
* now we switch arm clock to OSC, we can disable
|
||||
* pll2 now, gate pll2_pfd2 first.
|
||||
*/
|
||||
ldr r7, [r10, #0x100]
|
||||
orr r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
ldr r7, [r10, #0x30]
|
||||
orr r7, r7, #0x1000
|
||||
bic r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* enable pll2 and pll2_pfd2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
bic r7, r7, #0x1000
|
||||
orr r7, r7, #0x2000
|
||||
str r7, [r10, #0x30]
|
||||
|
||||
ldr r8, =0x30
|
||||
pll_do_wait_lock
|
||||
|
||||
ldr r7, [r10, #0x100]
|
||||
bic r7, #0x800000
|
||||
str r7, [r10, #0x100]
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_CCM_P_OFFSET]
|
||||
|
||||
/* set perclk back to 24MHz */
|
||||
ldr r7, [r10, #0x1c]
|
||||
bic r7, r7, #0x3f
|
||||
str r7, [r10, #0x1c]
|
||||
|
||||
/* set mmdc back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x7
|
||||
bic r7, r7, #(0x7 << 3)
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
/* set ahb div back to 24MHz */
|
||||
ldr r7, [r10, #0x14]
|
||||
bic r7, r7, #0x1c00
|
||||
str r7, [r10, #0x14]
|
||||
|
||||
ccm_do_wait
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
|
||||
/*
|
||||
* check whether any PLL is enabled, as only when
|
||||
* there is no PLLs enabled, 2P5 and 1P1 can be
|
||||
* off and only enable weak ones.
|
||||
*/
|
||||
|
||||
/* arm pll1 */
|
||||
ldr r7, [r10, #0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* sys pll2 */
|
||||
ldr r7, [r10, #0x30]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb pll3 */
|
||||
ldr r7, [r10, #0x10]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* audio pll4 */
|
||||
ldr r7, [r10, #0x70]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* vidio pll5 */
|
||||
ldr r7, [r10, #0xa0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enet pll6 */
|
||||
ldr r7, [r10, #0xe0]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* usb host pll7 */
|
||||
ldr r7, [r10, #0x20]
|
||||
ands r7, r7, #(1 << 31)
|
||||
bne 10f
|
||||
|
||||
/* enable weak 2P5 and turn off regular 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable weak 1p1 and turn off regular 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
|
||||
/* check whether ARM LDO is bypassed */
|
||||
ldr r7, [r10, #0x140]
|
||||
and r7, r7, #0x1f
|
||||
cmp r7, #0x1f
|
||||
bne 10f
|
||||
|
||||
/* low power band gap enable */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/*
|
||||
* clear the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
* Per RM, should be cleared when
|
||||
* band gap is powered down.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn off regular bandgap */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
10:
|
||||
/* switch to RC-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x40000000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* lower OSC current by 37.5% */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* disconnect vdd_high_in and vdd_snvs_in */
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x1000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_exit_idle
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
|
||||
/* increase OSC current to normal */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x6000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn on XTAL-OSC and detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x40000000
|
||||
orr r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* wait for XTAL stable */
|
||||
14:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, r7, #0x8000
|
||||
beq 14b
|
||||
|
||||
/* switch to XTAL-OSC */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x10
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* turn off XTAL-OSC detector */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x10000
|
||||
str r7, [r10, #0x150]
|
||||
15:
|
||||
/* check whether we need to enable 2P5/1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x40000
|
||||
beq 11f
|
||||
|
||||
/* check whether ARM LDO is bypassed */
|
||||
ldr r7, [r10, #0x140]
|
||||
and r7, r7, #0x1f
|
||||
cmp r7, #0x1f
|
||||
bne 12f
|
||||
|
||||
/* turn on regular bandgap and wait for stable */
|
||||
ldr r7, [r10, #0x150]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x150]
|
||||
13:
|
||||
ldr r7, [r10, #0x150]
|
||||
ands r7, #0x80
|
||||
beq 13b
|
||||
|
||||
/*
|
||||
* set the REFTOP_SELFBIASOFF,
|
||||
* self-bias circuit of the band gap.
|
||||
*/
|
||||
ldr r7, [r10, #0x150]
|
||||
orr r7, r7, #0x8
|
||||
str r7, [r10, #0x150]
|
||||
|
||||
/* turn on the bias current from the regular bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x80
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* low power band gap disable */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x20
|
||||
str r7, [r10, #0x270]
|
||||
12:
|
||||
/* enable regular 2P5 and turn off weak 2P5 */
|
||||
ldr r7, [r10, #0x130]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* Ensure the 2P5 is up. */
|
||||
3:
|
||||
ldr r7, [r10, #0x130]
|
||||
ands r7, r7, #0x20000
|
||||
beq 3b
|
||||
ldr r7, [r10, #0x130]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x130]
|
||||
|
||||
/* enable regular 1p1 and turn off weak 1P1 */
|
||||
ldr r7, [r10, #0x110]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x110]
|
||||
4:
|
||||
ldr r7, [r10, #0x110]
|
||||
ands r7, r7, #0x20000
|
||||
beq 4b
|
||||
ldr r7, [r10, #0x110]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x110]
|
||||
11:
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_enter_dvfs_mode
|
||||
|
||||
/* disable automatic power savings. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
/* disable power down timer */
|
||||
ldr r7, [r10, #0x4]
|
||||
bic r7, r7, #0xff00
|
||||
str r7, [r10, #0x4]
|
||||
|
||||
/* make the DDR explicitly enter self-refresh. */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
5:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
beq 5b
|
||||
|
||||
.endm
|
||||
|
||||
.macro resume_mmdc
|
||||
|
||||
/* restore MMDC IO */
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
|
||||
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
6:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 6b
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
|
||||
|
||||
/* reset read FIFO, RST_RD_FIFO */
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
7:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 7b
|
||||
|
||||
/* reset FIFO a second time */
|
||||
ldr r6, [r10, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r10, r7]
|
||||
8:
|
||||
ldr r6, [r10, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 8b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 21)
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
9:
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
bne 9b
|
||||
|
||||
/* enable power down timer */
|
||||
ldr r7, [r10, #0x4]
|
||||
orr r7, r7, #0x5500
|
||||
str r7, [r10, #0x4]
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_set_to_ocram
|
||||
|
||||
/* save ttbr */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
|
||||
/*
|
||||
* 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 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
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_back_to_ddr
|
||||
|
||||
/* 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
|
||||
|
||||
/* restore ttbr */
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.extern iram_tlb_phys_addr
|
||||
|
||||
/* imx6ull_low_power_idle */
|
||||
|
||||
.align 3
|
||||
ENTRY(imx6ull_low_power_idle)
|
||||
mx6ull_lpm_wfi_start:
|
||||
push {r4 - r10}
|
||||
|
||||
/* get necessary info from pm_info */
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r5, =imx6ull_low_power_idle
|
||||
ldr r6, =wakeup
|
||||
sub r6, r6, r5
|
||||
add r8, r1, r2
|
||||
add r3, r8, r6
|
||||
|
||||
/* store physical resume addr and pm_info address. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
|
||||
str r3, [r10, #0x20]
|
||||
str r1, [r10, #0x24]
|
||||
|
||||
/* set ARM power to be gated */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x1
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
tlb_set_to_ocram
|
||||
|
||||
/* make sure MMDC in self-refresh */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
mmdc_enter_dvfs_mode
|
||||
|
||||
/* save DDR IO settings */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldr r6, =0x0
|
||||
ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r8, r8, r0
|
||||
save_and_set_mmdc_io_lpm:
|
||||
ldr r9, [r8], #0x4
|
||||
ldr r5, [r10, r9]
|
||||
str r6, [r10, r9]
|
||||
str r5, [r8], #0x4
|
||||
subs r7, r7, #0x1
|
||||
bne save_and_set_mmdc_io_lpm
|
||||
|
||||
mov r5, #0x0
|
||||
ccm_enter_idle
|
||||
anatop_enter_idle
|
||||
|
||||
/*
|
||||
* mask all GPC interrupts before
|
||||
* enabling the RBC counters to
|
||||
* avoid the counter starting too
|
||||
* early if an interupt is already
|
||||
* pending.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r4, [r10, #MX6Q_GPC_IMR1]
|
||||
ldr r5, [r10, #MX6Q_GPC_IMR2]
|
||||
ldr r6, [r10, #MX6Q_GPC_IMR3]
|
||||
ldr r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
ldr r3, =0xffffffff
|
||||
str r3, [r10, #MX6Q_GPC_IMR1]
|
||||
str r3, [r10, #MX6Q_GPC_IMR2]
|
||||
str r3, [r10, #MX6Q_GPC_IMR3]
|
||||
str r3, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* enable the RBC bypass counter here
|
||||
* to hold off the interrupts. RBC counter
|
||||
* = 4 (120us). With this setting, the latency
|
||||
* from wakeup interrupt to ARM power up
|
||||
* is ~130uS.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
bic r3, r3, #(0x3f << 21)
|
||||
orr r3, r3, #(0x4 << 21)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* enable the counter. */
|
||||
ldr r3, [r10, #MX6Q_CCM_CCR]
|
||||
orr r3, r3, #(0x1 << 27)
|
||||
str r3, [r10, #MX6Q_CCM_CCR]
|
||||
|
||||
/* unmask all the GPC interrupts. */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
str r4, [r10, #MX6Q_GPC_IMR1]
|
||||
str r5, [r10, #MX6Q_GPC_IMR2]
|
||||
str r6, [r10, #MX6Q_GPC_IMR3]
|
||||
str r7, [r10, #MX6Q_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* now delay for a short while (3usec)
|
||||
* ARM is at 24MHz at this point
|
||||
* so a short loop should be enough.
|
||||
* this delay is required to ensure that
|
||||
* the RBC counter can start counting in
|
||||
* case an interrupt is already pending
|
||||
* or in case an interrupt arrives just
|
||||
* as ARM is about to assert DSM_request.
|
||||
*/
|
||||
ldr r4, =50
|
||||
rbc_loop:
|
||||
subs r4, r4, #0x1
|
||||
bne rbc_loop
|
||||
|
||||
wfi
|
||||
|
||||
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
|
||||
|
||||
mov r5, #0x0
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
resume_mmdc
|
||||
/* enable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
orr r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
|
||||
tlb_back_to_ddr
|
||||
|
||||
/* Restore registers */
|
||||
pop {r4 - r10}
|
||||
mov pc, lr
|
||||
|
||||
wakeup:
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r1, #0x0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r1, #0x1800
|
||||
mcr p15, 0, r1, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
/* clear core0's entry and parameter */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_SRC_P_OFFSET]
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #MX6Q_SRC_GPR1]
|
||||
str r7, [r10, #MX6Q_SRC_GPR2]
|
||||
|
||||
/* clear ARM power gate setting */
|
||||
ldr r10, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #0x2a0]
|
||||
|
||||
mov r5, #0x1
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
resume_mmdc
|
||||
|
||||
/* Restore registers */
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
mx6ull_lpm_wfi_end:
|
|
@ -0,0 +1,788 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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>
|
||||
|
||||
#define PM_INFO_VBASE_OFFSET 0x0
|
||||
#define PM_INFO_PBASE_OFFSET 0x4
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x8
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0xc
|
||||
#define PM_INFO_PM_INFO_TTBR_OFFSET 0x10
|
||||
#define PM_INFO_PM_INFO_NUM_ONLINE_CPUS_OFFSET 0x14
|
||||
#define PM_INFO_PM_INFO_NUM_LPI_CPUS_OFFSET 0x18
|
||||
#define PM_INFO_VAL_OFFSET 0x1c
|
||||
#define PM_INFO_FLAG0_OFFSET 0x20
|
||||
#define PM_INFO_FLAG1_OFFSET 0x24
|
||||
#define PM_INFO_MX7D_DDRC_P_OFFSET 0x28
|
||||
#define PM_INFO_MX7D_DDRC_V_OFFSET 0x2c
|
||||
#define PM_INFO_MX7D_CCM_P_OFFSET 0x30
|
||||
#define PM_INFO_MX7D_CCM_V_OFFSET 0x34
|
||||
#define PM_INFO_MX7D_ANATOP_P_OFFSET 0x38
|
||||
#define PM_INFO_MX7D_ANATOP_V_OFFSET 0x3c
|
||||
#define PM_INFO_MX7D_SRC_P_OFFSET 0x40
|
||||
#define PM_INFO_MX7D_SRC_V_OFFSET 0x44
|
||||
#define PM_INFO_MX7D_IOMUXC_GPR_P_OFFSET 0x48
|
||||
#define PM_INFO_MX7D_IOMUXC_GPR_V_OFFSET 0x4c
|
||||
#define PM_INFO_MX7D_GPC_P_OFFSET 0x50
|
||||
#define PM_INFO_MX7D_GPC_V_OFFSET 0x54
|
||||
#define PM_INFO_MX7D_GIC_DIST_P_OFFSET 0x58
|
||||
#define PM_INFO_MX7D_GIC_DIST_V_OFFSET 0x5c
|
||||
|
||||
#define MX7D_SRC_GPR1 0x74
|
||||
#define MX7D_SRC_GPR2 0x78
|
||||
#define MX7D_SRC_GPR3 0x7c
|
||||
#define MX7D_SRC_GPR4 0x80
|
||||
#define MX7D_GPC_IMR1 0x30
|
||||
#define MX7D_GPC_IMR2 0x34
|
||||
#define MX7D_GPC_IMR3 0x38
|
||||
#define MX7D_GPC_IMR4 0x3c
|
||||
#define DDRC_STAT 0x4
|
||||
#define DDRC_PWRCTL 0x30
|
||||
#define DDRC_DBG1 0x304
|
||||
#define DDRC_DBGCAM 0x308
|
||||
#define DDRC_PSTAT 0x3fc
|
||||
#define DDRC_PCTRL_0 0x490
|
||||
|
||||
/*
|
||||
* imx_pen_lock
|
||||
*
|
||||
* The reference link of Peterson's algorithm:
|
||||
* http://en.wikipedia.org/wiki/Peterson's_algorithm
|
||||
*
|
||||
* val1 = r1 = !turn (inverted from Peterson's algorithm)
|
||||
* on cpu 0:
|
||||
* r2 = flag[0] (in flag0)
|
||||
* r3 = flag[1] (in flag1)
|
||||
* on cpu1:
|
||||
* r2 = flag[1] (in flag1)
|
||||
* r3 = flag[0] (in flag0)
|
||||
*
|
||||
*/
|
||||
.macro imx_pen_lock
|
||||
|
||||
mov r8, r0
|
||||
mrc p15, 0, r5, c0, c0, 5
|
||||
and r5, r5, #3
|
||||
add r6, r8, #PM_INFO_VAL_OFFSET
|
||||
cmp r5, #0
|
||||
addeq r7, r8, #PM_INFO_FLAG0_OFFSET
|
||||
addeq r8, r8, #PM_INFO_FLAG1_OFFSET
|
||||
addne r7, r8, #PM_INFO_FLAG1_OFFSET
|
||||
addne r8, r8, #PM_INFO_FLAG0_OFFSET
|
||||
|
||||
mov r9, #1
|
||||
str r9, [r7]
|
||||
dsb
|
||||
str r5, [r6]
|
||||
1:
|
||||
dsb
|
||||
ldr r9, [r8]
|
||||
cmp r9, #1
|
||||
ldreq r9, [r6]
|
||||
cmpeq r9, r5
|
||||
beq 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro imx_pen_unlock
|
||||
|
||||
dsb
|
||||
mrc p15, 0, r6, c0, c0, 5
|
||||
and r6, r6, #3
|
||||
cmp r6, #0
|
||||
addeq r7, r0, #PM_INFO_FLAG0_OFFSET
|
||||
addne r7, r0, #PM_INFO_FLAG1_OFFSET
|
||||
mov r9, #0
|
||||
str r9, [r7]
|
||||
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
push {r0 - r12, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r12, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r12, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r12, lr}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
clrex
|
||||
|
||||
/* Turn off SMP bit. */
|
||||
mrc p15, 0, r8, c1, c0, 1
|
||||
bic r8, r8, #0x40
|
||||
mcr p15, 0, r8, c1, c0, 1
|
||||
|
||||
isb
|
||||
dsb
|
||||
#endif
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_set_to_ocram
|
||||
|
||||
/* save ttbr */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the BTAC. */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
ldr r6, =iram_tlb_phys_addr
|
||||
ldr r7, [r6]
|
||||
|
||||
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
|
||||
|
||||
.endm
|
||||
|
||||
.macro tlb_back_to_ddr
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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
|
||||
|
||||
/* restore ttbr */
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_TTBR_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
/* r10 must be DDRC base address */
|
||||
.macro ddrc_enter_self_refresh
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_DDRC_V_OFFSET]
|
||||
|
||||
/* disable port */
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #DDRC_PCTRL_0]
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #DDRC_PWRCTL]
|
||||
|
||||
/* wait rw port_busy clear */
|
||||
ldr r6, =(0x1 << 16)
|
||||
orr r6, r6, #0x1
|
||||
2:
|
||||
ldr r7, [r10, #DDRC_PSTAT]
|
||||
ands r7, r7, r6
|
||||
bne 2b
|
||||
|
||||
ldr r7, =0x1
|
||||
str r7, [r10, #DDRC_DBG1]
|
||||
|
||||
ldr r6, =0x36000000
|
||||
11:
|
||||
ldr r7, [r10, #DDRC_DBGCAM]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 11b
|
||||
|
||||
/* enter self-refresh bit 5 */
|
||||
ldr r7, =(0x1 << 5)
|
||||
str r7, [r10, #DDRC_PWRCTL]
|
||||
|
||||
/* wait until self-refresh mode entered */
|
||||
3:
|
||||
ldr r7, [r10, #DDRC_STAT]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x3
|
||||
bne 3b
|
||||
4:
|
||||
ldr r7, [r10, #DDRC_STAT]
|
||||
ands r7, r7, #0x20
|
||||
beq 4b
|
||||
|
||||
/* disable dram clk */
|
||||
ldr r7, [r10, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 3)
|
||||
str r7, [r10, #DDRC_PWRCTL]
|
||||
|
||||
/*
|
||||
* TO1.1 adds feature of DDR pads power down,
|
||||
* although TO1.0 has no such function, but it is
|
||||
* NOT harmful to program GPR registers for TO1.0,
|
||||
* it can avoid the logic of version check in idle
|
||||
* thread.
|
||||
*/
|
||||
ldr r10, [r0, #PM_INFO_MX7D_IOMUXC_GPR_V_OFFSET]
|
||||
ldr r7, =0xf0000
|
||||
str r7, [r10]
|
||||
|
||||
/* delay 20us, measured by gpio */
|
||||
ldr r7, =20
|
||||
12:
|
||||
subs r7, r7, #0x1
|
||||
bne 12b
|
||||
|
||||
.endm
|
||||
|
||||
/* r10 must be DDRC base address */
|
||||
.macro ddrc_exit_self_refresh
|
||||
|
||||
cmp r5, #0x1
|
||||
ldreq r10, [r0, #PM_INFO_MX7D_IOMUXC_GPR_P_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX7D_IOMUXC_GPR_V_OFFSET]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r10]
|
||||
|
||||
ldr r7, =20
|
||||
13:
|
||||
subs r7, r7, #0x1
|
||||
bne 13b
|
||||
|
||||
cmp r5, #0x1
|
||||
ldreq r10, [r0, #PM_INFO_MX7D_DDRC_P_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX7D_DDRC_V_OFFSET]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #DDRC_DBG1]
|
||||
|
||||
ldr r6, =0x30000000
|
||||
14:
|
||||
ldr r7, [r10, #DDRC_DBGCAM]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 14b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, =0x0
|
||||
str r7, [r10, #DDRC_PWRCTL]
|
||||
|
||||
/* wait until self-refresh mode exited */
|
||||
5:
|
||||
ldr r7, [r10, #DDRC_STAT]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x3
|
||||
beq 5b
|
||||
|
||||
/* enable auto self-refresh */
|
||||
ldr r7, [r10, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 0)
|
||||
str r7, [r10, #DDRC_PWRCTL]
|
||||
|
||||
ldr r7, =0x1
|
||||
str r7, [r10, #DDRC_PCTRL_0]
|
||||
|
||||
.endm
|
||||
|
||||
.macro pll_do_wait_lock
|
||||
6:
|
||||
ldr r7, [r10, r8]
|
||||
ands r7, #0x80000000
|
||||
beq 6b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_ANATOP_V_OFFSET]
|
||||
|
||||
/* ungate pfd1 332m for lower axi */
|
||||
ldr r7, =0x8000
|
||||
str r7, [r10, #0xc8]
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_CCM_V_OFFSET]
|
||||
|
||||
/* switch ARM CLK to OSC */
|
||||
ldr r8, =0x8000
|
||||
ldr r7, [r10, r8]
|
||||
bic r7, r7, #0x7000000
|
||||
str r7, [r10, r8]
|
||||
|
||||
/* lower AXI clk from 24MHz to 3MHz */
|
||||
ldr r8, =0x8800
|
||||
ldr r7, [r10, r8]
|
||||
orr r7, r7, #0x7
|
||||
str r7, [r10, r8]
|
||||
|
||||
/* lower AHB clk from 24MHz to 3MHz */
|
||||
ldr r8, =0x9000
|
||||
ldr r7, [r10, r8]
|
||||
orr r7, r7, #0x7
|
||||
str r7, [r10, r8]
|
||||
|
||||
/* gate dram clk */
|
||||
ldr r8, =0x9880
|
||||
ldr r7, [r10, r8]
|
||||
bic r7, r7, #0x10000000
|
||||
str r7, [r10, r8]
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_ANATOP_V_OFFSET]
|
||||
|
||||
/* gate pfd1 332m */
|
||||
ldr r7, =0x8000
|
||||
str r7, [r10, #0xc4]
|
||||
|
||||
/* gate system pll pfd div 1 */
|
||||
ldr r7, =0x10
|
||||
str r7, [r10, #0xb4]
|
||||
/* power down ARM, 480 and DRAM PLL */
|
||||
ldr r7, =0x1000
|
||||
str r7, [r10, #0x64]
|
||||
str r7, [r10, #0xb4]
|
||||
ldr r7, =0x100000
|
||||
str r7, [r10, #0x74]
|
||||
|
||||
.endm
|
||||
|
||||
.macro ccm_exit_idle
|
||||
|
||||
cmp r5, #0x1
|
||||
ldreq r10, [r0, #PM_INFO_MX7D_ANATOP_P_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX7D_ANATOP_V_OFFSET]
|
||||
|
||||
/* power up ARM, 480 and DRAM PLL */
|
||||
ldr r7, =0x1000
|
||||
str r7, [r10, #0x68]
|
||||
ldr r8, =0x60
|
||||
pll_do_wait_lock
|
||||
|
||||
ldr r7, =0x1000
|
||||
str r7, [r10, #0xb8]
|
||||
ldr r8, =0xb0
|
||||
pll_do_wait_lock
|
||||
|
||||
ldr r7, =0x100000
|
||||
str r7, [r10, #0x78]
|
||||
ldr r8, =0x70
|
||||
pll_do_wait_lock
|
||||
|
||||
/* ungate pfd1 332m for lower axi */
|
||||
ldr r7, =0x8000
|
||||
str r7, [r10, #0xc8]
|
||||
|
||||
/* ungate system pll pfd div 1 */
|
||||
ldr r7, =0x10
|
||||
str r7, [r10, #0xb8]
|
||||
|
||||
cmp r5, #0x1
|
||||
ldreq r10, [r0, #PM_INFO_MX7D_CCM_P_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX7D_CCM_V_OFFSET]
|
||||
|
||||
/* switch ARM CLK to PLL */
|
||||
ldr r8, =0x8000
|
||||
ldr r7, [r10, r8]
|
||||
orr r7, r7, #0x1000000
|
||||
str r7, [r10, r8]
|
||||
|
||||
/* restore AXI clk from 3MHz to 24MHz */
|
||||
ldr r8, =0x8800
|
||||
ldr r7, [r10, r8]
|
||||
bic r7, r7, #0x7
|
||||
str r7, [r10, r8]
|
||||
|
||||
/* restore AHB clk from 3MHz to 24MHz */
|
||||
ldr r8, =0x9000
|
||||
ldr r7, [r10, r8]
|
||||
bic r7, r7, #0x7
|
||||
str r7, [r10, r8]
|
||||
|
||||
/* ungate dram clk */
|
||||
ldr r8, =0x9880
|
||||
ldr r7, [r10, r8]
|
||||
orr r7, r7, #0x10000000
|
||||
str r7, [r10, r8]
|
||||
|
||||
cmp r5, #0x1
|
||||
ldreq r10, [r0, #PM_INFO_MX7D_ANATOP_P_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX7D_ANATOP_V_OFFSET]
|
||||
|
||||
/* gate pfd1 332m for lower axi */
|
||||
ldr r7, =0x8000
|
||||
str r7, [r10, #0xc4]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_ANATOP_V_OFFSET]
|
||||
|
||||
/* XTAL to RC-OSC switch */
|
||||
ldr r7, [r10]
|
||||
orr r7, r7, #0x1000
|
||||
str r7, [r10]
|
||||
/* power down XTAL */
|
||||
ldr r7, [r10]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10]
|
||||
|
||||
/* enable weak 1P0A */
|
||||
ldr r7, [r10, #0x200]
|
||||
orr r7, r7, #0x40000
|
||||
str r7, [r10, #0x200]
|
||||
|
||||
/* disable LDO 1P0A */
|
||||
ldr r7, [r10, #0x200]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x200]
|
||||
|
||||
/* disable LDO 1P0D */
|
||||
ldr r7, [r10, #0x210]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x210]
|
||||
|
||||
/* disable LDO 1P2 */
|
||||
ldr r7, [r10, #0x220]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x220]
|
||||
|
||||
/* switch to low power bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
orr r7, r7, #0x400
|
||||
str r7, [r10, #0x270]
|
||||
/* power down normal bandgap */
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
.endm
|
||||
|
||||
.macro anatop_exit_idle
|
||||
|
||||
cmp r5, #0x1
|
||||
ldreq r10, [r0, #PM_INFO_MX7D_ANATOP_P_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX7D_ANATOP_V_OFFSET]
|
||||
|
||||
/* power on normal bandgap */
|
||||
ldr r7, [r10, #0x270]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #0x270]
|
||||
/* switch to normal bandgap */
|
||||
bic r7, r7, #0x400
|
||||
str r7, [r10, #0x270]
|
||||
|
||||
/* enable LDO 1P2 */
|
||||
ldr r7, [r10, #0x220]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x220]
|
||||
7:
|
||||
ldr r7, [r10, #0x220]
|
||||
ands r7, #0x20000
|
||||
beq 7b
|
||||
|
||||
/* enable LDO 1P0D */
|
||||
ldr r7, [r10, #0x210]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x210]
|
||||
8:
|
||||
ldr r7, [r10, #0x210]
|
||||
ands r7, #0x20000
|
||||
beq 8b
|
||||
|
||||
/* enable LDO 1P0A */
|
||||
ldr r7, [r10, #0x200]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r10, #0x200]
|
||||
9:
|
||||
ldr r7, [r10, #0x200]
|
||||
ands r7, #0x20000
|
||||
beq 9b
|
||||
/* disable weak 1P0A */
|
||||
ldr r7, [r10, #0x200]
|
||||
bic r7, r7, #0x40000
|
||||
str r7, [r10, #0x200]
|
||||
|
||||
/* power up XTAL and wait */
|
||||
ldr r7, [r10]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10]
|
||||
10:
|
||||
ldr r7, [r10]
|
||||
ands r7, r7, #0x4
|
||||
beq 10b
|
||||
/* RC-OSC to XTAL switch */
|
||||
ldr r7, [r10]
|
||||
bic r7, r7, #0x1000
|
||||
str r7, [r10]
|
||||
|
||||
.endm
|
||||
|
||||
.extern iram_tlb_phys_addr
|
||||
|
||||
.align 3
|
||||
ENTRY(imx7d_low_power_idle)
|
||||
push {r0 - r12}
|
||||
|
||||
/* get necessary info from pm_info */
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r5, =imx7d_low_power_idle
|
||||
ldr r6, =wakeup
|
||||
sub r6, r6, r5
|
||||
add r8, r1, r2
|
||||
add r3, r8, r6
|
||||
|
||||
/* r11 is cpu id */
|
||||
mrc p15, 0, r11, c0, c0, 5
|
||||
and r11, r11, #3
|
||||
cmp r11, #0x0
|
||||
ldreq r6, =MX7D_SRC_GPR1
|
||||
ldreq r7, =MX7D_SRC_GPR2
|
||||
ldrne r6, =MX7D_SRC_GPR3
|
||||
ldrne r7, =MX7D_SRC_GPR4
|
||||
/* store physical resume addr and pm_info address. */
|
||||
ldr r10, [r0, #PM_INFO_MX7D_SRC_V_OFFSET]
|
||||
str r3, [r10, r6]
|
||||
str r1, [r10, r7]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
tlb_set_to_ocram
|
||||
|
||||
/* check last to sleep */
|
||||
ldr r6, [r0, #PM_INFO_PM_INFO_NUM_ONLINE_CPUS_OFFSET]
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_NUM_LPI_CPUS_OFFSET]
|
||||
cmp r6, r7
|
||||
bne lpi_enter_done
|
||||
|
||||
ddrc_enter_self_refresh
|
||||
ccm_enter_idle
|
||||
anatop_enter_idle
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_GIC_DIST_V_OFFSET]
|
||||
ldr r7, =0x0
|
||||
ldr r8, =0x1000
|
||||
str r7, [r10, r8]
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_GPC_V_OFFSET]
|
||||
ldr r4, [r10, #MX7D_GPC_IMR1]
|
||||
ldr r5, [r10, #MX7D_GPC_IMR2]
|
||||
ldr r6, [r10, #MX7D_GPC_IMR3]
|
||||
ldr r7, [r10, #MX7D_GPC_IMR4]
|
||||
|
||||
ldr r8, =0xffffffff
|
||||
str r8, [r10, #MX7D_GPC_IMR1]
|
||||
str r8, [r10, #MX7D_GPC_IMR2]
|
||||
str r8, [r10, #MX7D_GPC_IMR3]
|
||||
str r8, [r10, #MX7D_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* enable the RBC bypass counter here
|
||||
* to hold off the interrupts. RBC counter
|
||||
* = 8 (240us). With this setting, the latency
|
||||
* from wakeup interrupt to ARM power up
|
||||
* is ~250uS.
|
||||
*/
|
||||
ldr r8, [r10, #0x14]
|
||||
bic r8, r8, #(0x3f << 24)
|
||||
orr r8, r8, #(0x8 << 24)
|
||||
str r8, [r10, #0x14]
|
||||
|
||||
/* enable the counter. */
|
||||
ldr r8, [r10, #0x14]
|
||||
orr r8, r8, #(0x1 << 30)
|
||||
str r8, [r10, #0x14]
|
||||
|
||||
/* unmask all the GPC interrupts. */
|
||||
str r4, [r10, #MX7D_GPC_IMR1]
|
||||
str r5, [r10, #MX7D_GPC_IMR2]
|
||||
str r6, [r10, #MX7D_GPC_IMR3]
|
||||
str r7, [r10, #MX7D_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* now delay for a short while (30usec)
|
||||
* ARM is at 24MHz at this point
|
||||
* so a short loop should be enough.
|
||||
* this delay is required to ensure that
|
||||
* the RBC counter can start counting in
|
||||
* case an interrupt is already pending
|
||||
* or in case an interrupt arrives just
|
||||
* as ARM is about to assert DSM_request.
|
||||
*/
|
||||
ldr r4, =5
|
||||
rbc_loop:
|
||||
subs r4, r4, #0x1
|
||||
bne rbc_loop
|
||||
|
||||
lpi_enter_done:
|
||||
|
||||
imx_pen_unlock
|
||||
|
||||
wfi
|
||||
|
||||
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
|
||||
|
||||
imx_pen_lock
|
||||
|
||||
/* check first to wake */
|
||||
ldr r6, [r0, #PM_INFO_PM_INFO_NUM_ONLINE_CPUS_OFFSET]
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_NUM_LPI_CPUS_OFFSET]
|
||||
cmp r6, r7
|
||||
bne skip_lpi_flow
|
||||
|
||||
ldr r5, =0x0
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
ddrc_exit_self_refresh
|
||||
|
||||
ldr r10, [r0, #PM_INFO_MX7D_GIC_DIST_V_OFFSET]
|
||||
ldr r7, =0x1
|
||||
ldr r8, =0x1000
|
||||
str r7, [r10, r8]
|
||||
|
||||
skip_lpi_flow:
|
||||
tlb_back_to_ddr
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Turn on SMP bit. */
|
||||
mrc p15, 0, r7, c1, c0, 1
|
||||
orr r7, r7, #0x40
|
||||
mcr p15, 0, r7, c1, c0, 1
|
||||
|
||||
isb
|
||||
#endif
|
||||
|
||||
/* enable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
orr r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Restore registers */
|
||||
pop {r0 - r12}
|
||||
mov pc, lr
|
||||
|
||||
wakeup:
|
||||
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r1, #0x0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
mcr p15, 0, r1, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r1, #0x1800
|
||||
mcr p15, 0, r1, c1, c0, 0
|
||||
isb
|
||||
|
||||
imx_pen_lock
|
||||
|
||||
/* check first to wake */
|
||||
ldr r6, [r0, #PM_INFO_PM_INFO_NUM_ONLINE_CPUS_OFFSET]
|
||||
ldr r7, [r0, #PM_INFO_PM_INFO_NUM_LPI_CPUS_OFFSET]
|
||||
cmp r6, r7
|
||||
bne wakeup_skip_lpi_flow
|
||||
|
||||
ldr r5, =0x1
|
||||
anatop_exit_idle
|
||||
ccm_exit_idle
|
||||
ddrc_exit_self_refresh
|
||||
|
||||
wakeup_skip_lpi_flow:
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
|
||||
/* Restore registers */
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
ENDPROC(imx7d_low_power_idle)
|
|
@ -0,0 +1,618 @@
|
|||
/*
|
||||
* Copyright (C) 2012-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 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
|
||||
|
||||
.globl imx6_lpddr2_freq_change_start
|
||||
.globl imx6_lpddr2_freq_change_end
|
||||
|
||||
.macro mx6sl_switch_to_24MHz
|
||||
|
||||
/*
|
||||
* Set MMDC clock to be sourced from PLL3.
|
||||
* Ensure first periph2_clk2 is sourced from PLL3.
|
||||
* Set the PERIPH2_CLK2_PODF to divide by 2.
|
||||
*/
|
||||
ldr r6, [r2, #0x14]
|
||||
bic r6, r6, #0x7
|
||||
orr r6, r6, #0x1
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
/* Select PLL3 to source MMDC. */
|
||||
ldr r6, [r2, #0x18]
|
||||
bic r6, r6, #0x100000
|
||||
str r6, [r2, #0x18]
|
||||
|
||||
/* Swtich periph2_clk_sel to run from PLL3. */
|
||||
ldr r6, [r2, #0x14]
|
||||
orr r6, r6, #0x4000000
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
periph2_clk_switch1:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne periph2_clk_switch1
|
||||
|
||||
/*
|
||||
* Need to clock gate the 528 PFDs before
|
||||
* powering down PLL2.
|
||||
* Only the PLL2_PFD2_400M should be ON
|
||||
* at this time, so only clock gate that one.
|
||||
*/
|
||||
ldr r6, [r3, #0x100]
|
||||
orr r6, r6, #0x800000
|
||||
str r6, [r3, #0x100]
|
||||
|
||||
/*
|
||||
* Set PLL2 to bypass state. We should be here
|
||||
* only if MMDC is not sourced from PLL2.
|
||||
*/
|
||||
ldr r6, [r3, #0x30]
|
||||
orr r6, r6, #0x10000
|
||||
str r6, [r3, #0x30]
|
||||
|
||||
ldr r6, [r3, #0x30]
|
||||
orr r6, r6, #0x1000
|
||||
str r6, [r3, #0x30]
|
||||
|
||||
/* Ensure pre_periph2_clk_mux is set to pll2 */
|
||||
ldr r6, [r2, #0x18]
|
||||
bic r6, r6, #0x600000
|
||||
str r6, [r2, #0x18]
|
||||
|
||||
/* Set MMDC clock to be sourced from the bypassed PLL2. */
|
||||
ldr r6, [r2, #0x14]
|
||||
bic r6, r6, #0x4000000
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
periph2_clk_switch2:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne periph2_clk_switch2
|
||||
|
||||
/*
|
||||
* Now move MMDC back to periph2_clk2 source.
|
||||
* after selecting PLL2 as the option.
|
||||
* Select PLL2 as the source.
|
||||
*/
|
||||
ldr r6, [r2, #0x18]
|
||||
orr r6, r6, #0x100000
|
||||
str r6, [r2, #0x18]
|
||||
|
||||
/* set periph2_clk2_podf to divide by 1. */
|
||||
ldr r6, [r2, #0x14]
|
||||
bic r6, r6, #0x7
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
/* Now move periph2_clk to periph2_clk2 source */
|
||||
ldr r6, [r2, #0x14]
|
||||
orr r6, r6, #0x4000000
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
periph2_clk_switch3:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne periph2_clk_switch3
|
||||
|
||||
/* Now set the MMDC PODF back to 1.*/
|
||||
ldr r6, [r2, #0x14]
|
||||
bic r6, r6, #0x38
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
mmdc_podf0:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne mmdc_podf0
|
||||
|
||||
.endm
|
||||
|
||||
.macro ddr_switch_400MHz
|
||||
|
||||
/* Set MMDC divider first, in case PLL3 is at 480MHz. */
|
||||
ldr r6, [r3, #0x10]
|
||||
and r6, r6, #0x10000
|
||||
cmp r6, #0x10000
|
||||
beq pll3_in_bypass
|
||||
|
||||
/* Set MMDC divder to divide by 2. */
|
||||
ldr r6, [r2, #0x14]
|
||||
bic r6, r6, #0x38
|
||||
orr r6, r6, #0x8
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
mmdc_podf:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne mmdc_podf
|
||||
|
||||
pll3_in_bypass:
|
||||
/*
|
||||
* Check if we are switching between
|
||||
* 400Mhz <-> 100MHz.If so, we should
|
||||
* try to source MMDC from PLL2_200M.
|
||||
*/
|
||||
cmp r1, #0
|
||||
beq not_low_bus_freq
|
||||
|
||||
/* Ensure that MMDC is sourced from PLL2 mux first. */
|
||||
ldr r6, [r2, #0x14]
|
||||
bic r6, r6, #0x4000000
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
periph2_clk_switch4:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne periph2_clk_switch4
|
||||
|
||||
not_low_bus_freq:
|
||||
/* Now ensure periph2_clk2_sel mux is set to PLL3 */
|
||||
ldr r6, [r2, #0x18]
|
||||
bic r6, r6, #0x100000
|
||||
str r6, [r2, #0x18]
|
||||
|
||||
/* Now switch MMDC to PLL3. */
|
||||
ldr r6, [r2, #0x14]
|
||||
orr r6, r6, #0x4000000
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
periph2_clk_switch5:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne periph2_clk_switch5
|
||||
|
||||
/*
|
||||
* Check if PLL2 is already unlocked.
|
||||
* If so do nothing with PLL2.
|
||||
*/
|
||||
cmp r1, #0
|
||||
beq pll2_already_on
|
||||
|
||||
/* Now power up PLL2 and unbypass it. */
|
||||
ldr r6, [r3, #0x30]
|
||||
bic r6, r6, #0x1000
|
||||
str r6, [r3, #0x30]
|
||||
|
||||
/* Make sure PLL2 has locked.*/
|
||||
wait_for_pll_lock:
|
||||
ldr r6, [r3, #0x30]
|
||||
and r6, r6, #0x80000000
|
||||
cmp r6, #0x80000000
|
||||
bne wait_for_pll_lock
|
||||
|
||||
ldr r6, [r3, #0x30]
|
||||
bic r6, r6, #0x10000
|
||||
str r6, [r3, #0x30]
|
||||
|
||||
/*
|
||||
* Need to enable the 528 PFDs after
|
||||
* powering up PLL2.
|
||||
* Only the PLL2_PFD2_400M should be ON
|
||||
* as it feeds the MMDC. Rest should have
|
||||
* been managed by clock code.
|
||||
*/
|
||||
ldr r6, [r3, #0x100]
|
||||
bic r6, r6, #0x800000
|
||||
str r6, [r3, #0x100]
|
||||
|
||||
pll2_already_on:
|
||||
/*
|
||||
* Now switch MMDC clk back to pll2_mux option.
|
||||
* Ensure pre_periph2_clk2 is set to pll2_pfd_400M.
|
||||
* If switching to audio DDR freq, set the
|
||||
* pre_periph2_clk2 to PLL2_PFD_200M
|
||||
*/
|
||||
ldr r6, =400000000
|
||||
cmp r6, r0
|
||||
bne use_pll2_pfd_200M
|
||||
|
||||
ldr r6, [r2, #0x18]
|
||||
bic r6, r6, #0x600000
|
||||
orr r6, r6, #0x200000
|
||||
str r6, [r2, #0x18]
|
||||
ldr r6, =400000000
|
||||
b cont2
|
||||
|
||||
use_pll2_pfd_200M:
|
||||
ldr r6, [r2, #0x18]
|
||||
orr r6, r6, #0x600000
|
||||
str r6, [r2, #0x18]
|
||||
ldr r6, =200000000
|
||||
|
||||
cont2:
|
||||
ldr r4, [r2, #0x14]
|
||||
bic r4, r4, #0x4000000
|
||||
str r4, [r2, #0x14]
|
||||
|
||||
periph2_clk_switch6:
|
||||
ldr r4, [r2, #0x48]
|
||||
cmp r4, #0
|
||||
bne periph2_clk_switch6
|
||||
|
||||
change_divider_only:
|
||||
/*
|
||||
* Calculate the MMDC divider
|
||||
* based on the requested freq.
|
||||
*/
|
||||
ldr r4, =0
|
||||
Loop2:
|
||||
sub r6, r6, r0
|
||||
cmp r6, r0
|
||||
blt Div_Found
|
||||
add r4, r4, #1
|
||||
bgt Loop2
|
||||
|
||||
/* Shift divider into correct offset. */
|
||||
lsl r4, r4, #3
|
||||
Div_Found:
|
||||
/* Set the MMDC PODF. */
|
||||
ldr r6, [r2, #0x14]
|
||||
bic r6, r6, #0x38
|
||||
orr r6, r6, r4
|
||||
str r6, [r2, #0x14]
|
||||
|
||||
mmdc_podf1:
|
||||
ldr r6, [r2, #0x48]
|
||||
cmp r6, #0
|
||||
bne mmdc_podf1
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_clk_lower_100MHz
|
||||
|
||||
/*
|
||||
* Prior to reducing the DDR frequency (at 528/400 MHz),
|
||||
* read the Measure unit count bits (MU_UNIT_DEL_NUM)
|
||||
*/
|
||||
ldr r5, =0x8B8
|
||||
ldr r6, [r8, r5]
|
||||
/* 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, [r8, r5]
|
||||
orr r6, r6, #0x400
|
||||
str r6, [r8, r5]
|
||||
/*
|
||||
* 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, [r8, r5]
|
||||
ldr r4, =0x3FF
|
||||
bic r6, r6, r4
|
||||
orr r6, r6, r7
|
||||
str r6, [r8, r5]
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_clk_above_100MHz
|
||||
|
||||
/* Make sure that the PHY measurement unit is NOT in bypass mode */
|
||||
ldr r5, =0x8B8
|
||||
ldr r6, [r8, r5]
|
||||
bic r6, r6, #0x400
|
||||
str r6, [r8, r5]
|
||||
/* Now perform a Force Measurement. */
|
||||
ldr r6, [r8, r5]
|
||||
orr r6, r6, #0x800
|
||||
str r6, [r8, r5]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
force_measure1:
|
||||
ldr r6, [r8, r5]
|
||||
and r6, r6, #0x800
|
||||
cmp r6, #0x0
|
||||
bne force_measure1
|
||||
.endm
|
||||
|
||||
/*
|
||||
* mx6_lpddr2_freq_change
|
||||
*
|
||||
* Make sure DDR is in self-refresh.
|
||||
* IRQs are already disabled.
|
||||
* r0 : DDR freq.
|
||||
* r1: low_bus_freq_mode flag
|
||||
*/
|
||||
.align 3
|
||||
ENTRY(mx6_lpddr2_freq_change)
|
||||
imx6_lpddr2_freq_change_start:
|
||||
push {r4-r10}
|
||||
|
||||
/*
|
||||
* 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]
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the Branch Target Address Cache (BTAC) */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
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
|
||||
|
||||
|
||||
#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)
|
||||
|
||||
/* Wait for background operations to complete. */
|
||||
wait_for_l2_to_idle:
|
||||
ldr r6, [r7, #0x730]
|
||||
cmp r6, #0x0
|
||||
bne wait_for_l2_to_idle
|
||||
|
||||
mov r6, #0x0
|
||||
str r6, [r7, #0x730]
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
ldr r3, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR)
|
||||
ldr r2, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
|
||||
ldr r8, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
|
||||
|
||||
/* Disable Automatic power savings. */
|
||||
ldr r6, [r8, #0x404]
|
||||
orr r6, r6, #0x01
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
/* MMDC0_MDPDC disable power down timer */
|
||||
ldr r6, [r8, #0x4]
|
||||
bic r6, r6, #0xff00
|
||||
str r6, [r8, #0x4]
|
||||
|
||||
/* Delay for a while */
|
||||
ldr r10, =10
|
||||
delay1:
|
||||
ldr r7, =0
|
||||
cont1:
|
||||
ldr r6, [r8, r7]
|
||||
add r7, r7, #4
|
||||
cmp r7, #16
|
||||
bne cont1
|
||||
sub r10, r10, #1
|
||||
cmp r10, #0
|
||||
bgt delay1
|
||||
|
||||
/* Make the DDR explicitly enter self-refresh. */
|
||||
ldr r6, [r8, #0x404]
|
||||
orr r6, r6, #0x200000
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
poll_dvfs_set_1:
|
||||
ldr r6, [r8, #0x404]
|
||||
and r6, r6, #0x2000000
|
||||
cmp r6, #0x2000000
|
||||
bne poll_dvfs_set_1
|
||||
|
||||
/* set SBS step-by-step mode */
|
||||
ldr r6, [r8, #0x410]
|
||||
orr r6, r6, #0x100
|
||||
str r6, [r8, #0x410]
|
||||
|
||||
ldr r10, =100000000
|
||||
cmp r0, r10
|
||||
bgt set_ddr_mu_above_100
|
||||
mmdc_clk_lower_100MHz
|
||||
|
||||
set_ddr_mu_above_100:
|
||||
ldr r10, =24000000
|
||||
cmp r0, r10
|
||||
beq set_to_24MHz
|
||||
|
||||
ddr_switch_400MHz
|
||||
|
||||
ldr r10,=100000000
|
||||
cmp r0, r10
|
||||
blt done
|
||||
mmdc_clk_above_100MHz
|
||||
|
||||
b done
|
||||
|
||||
set_to_24MHz:
|
||||
mx6sl_switch_to_24MHz
|
||||
|
||||
done:
|
||||
/* clear DVFS - exit from self refresh mode */
|
||||
ldr r6, [r8, #0x404]
|
||||
bic r6, r6, #0x200000
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
poll_dvfs_clear_1:
|
||||
ldr r6, [r8, #0x404]
|
||||
and r6, r6, #0x2000000
|
||||
cmp r6, #0x2000000
|
||||
beq poll_dvfs_clear_1
|
||||
|
||||
/* Enable Automatic power savings. */
|
||||
ldr r6, [r8, #0x404]
|
||||
bic r6, r6, #0x01
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
ldr r10, =24000000
|
||||
cmp r0, r10
|
||||
beq skip_power_down
|
||||
|
||||
/* Enable MMDC power down timer. */
|
||||
ldr r6, [r8, #0x4]
|
||||
orr r6, r6, #0x5500
|
||||
str r6, [r8, #0x4]
|
||||
|
||||
skip_power_down:
|
||||
/* clear SBS - unblock DDR accesses */
|
||||
ldr r6, [r8, #0x410]
|
||||
bic r6, r6, #0x100
|
||||
str r6, [r8, #0x410]
|
||||
|
||||
#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
|
||||
|
||||
/* 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
|
||||
|
||||
pop {r4-r10}
|
||||
|
||||
/* Restore registers */
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* Add ltorg here to ensure that all
|
||||
* literals are stored here and are
|
||||
* within the text space.
|
||||
*/
|
||||
.ltorg
|
||||
imx6_lpddr2_freq_change_end:
|
|
@ -0,0 +1,765 @@
|
|||
/*
|
||||
* Copyright (C) 2015-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 <asm/smp_scu.h>
|
||||
#include "hardware.h"
|
||||
|
||||
#define CCM_CBCDR 0x14
|
||||
#define CCM_CBCMR 0x18
|
||||
#define CCM_CSCMR1 0x1c
|
||||
#define CCM_CDHIPR 0x48
|
||||
|
||||
.globl mx6q_lpddr2_freq_change_start
|
||||
.globl mx6q_lpddr2_freq_change_end
|
||||
|
||||
.macro wait_for_ccm_handshake
|
||||
/* wait for div update */
|
||||
1:
|
||||
ldr r9, [r2, #CCM_CDHIPR]
|
||||
cmp r9, #0
|
||||
bne 1b
|
||||
|
||||
.endm
|
||||
|
||||
.macro set_mmdc_misc_ralat_2_cycles
|
||||
|
||||
/* Set MMDCx_MISC[RALAT] = 2 cycles */
|
||||
ldr r6, [r8, #0x18]
|
||||
bic r6, r6, #(0x7 << 6)
|
||||
orr r6, r6, #(0x2 << 6)
|
||||
str r6, [r8, #0x18]
|
||||
|
||||
/* Check if lpddr2 channel 1 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq 1f
|
||||
|
||||
ldr r6, [r4, #0x18]
|
||||
bic r6, r6, #(0x7 << 6)
|
||||
orr r6, r6, #(0x2 << 6)
|
||||
str r6, [r4, #0x18]
|
||||
1:
|
||||
.endm
|
||||
|
||||
.macro switch_to_400MHz
|
||||
/* set the MMDC_DIV=1, AXI_DIV=2, AHB_DIV=3 */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
ldr r6, =0x3f1f00
|
||||
bic r9, r9, r6
|
||||
orr r9, r9, #(0x9 << 8)
|
||||
orr r9, r9, #(1 << 16)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
/* check periph_clk_sel */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
and r9, r9, #(1 << 25)
|
||||
cmp r9, #(1 << 25)
|
||||
bne skip_periph_clk_switch_400m
|
||||
|
||||
/* now switch periph_clk back. */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
bic r9, r9, #(1 << 25)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
skip_periph_clk_switch_400m:
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_100MHz
|
||||
/* set the MMDC_DIV=4, AXI_DIV=8, AHB_DIV=8 */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
ldr r6, =0x3f1f00
|
||||
bic r9, r9, r6
|
||||
orr r9, r9, #(0x1F << 16)
|
||||
orr r9, r9, #(0x1D << 8)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
/* check if periph_clk_sel is already set. */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
and r9, r9, #(1 << 25)
|
||||
cmp r9, #(1 << 25)
|
||||
bne skip_periph_clk_switch_100m
|
||||
|
||||
/* now switch periph_clk back. */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
bic r9, r9, #(1 << 25)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
skip_periph_clk_switch_100m:
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_24MHz
|
||||
/*
|
||||
* change the freq now try setting DDR to 24MHz.
|
||||
* source it from the periph_clk2 ensure the
|
||||
* periph_clk2 is sourced from 24MHz and the
|
||||
* divider is 1.
|
||||
*/
|
||||
|
||||
ldr r9, [r2, #CCM_CBCMR]
|
||||
bic r9, r9, #(0x3 << 12)
|
||||
orr r9, r9, #(1 << 12)
|
||||
str r9, [r2, #CCM_CBCMR]
|
||||
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
bic r9, r9, #(0x7 << 27)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
/* now switch periph_clk to 24MHz. */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
orr r9, r9, #(1 << 25)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
/* change all the dividers to 1. */
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
ldr r6, =0x3f1f00
|
||||
bic r9, r9, r6
|
||||
orr r9, r9, #(1 << 8)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
/* Wait for the divider to change. */
|
||||
wait_for_ccm_handshake
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_24MHZ_from_pll2
|
||||
/* Change DDR freq settings from pll2_pfd2 (div 2) */
|
||||
|
||||
ldr r9, [r2, #CCM_CBCMR]
|
||||
bic r9, r9, #(0x3 << 18)
|
||||
orr r9, r9, #(0x3 << 18)
|
||||
str r9, [r2, #CCM_CBCMR]
|
||||
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
bic r9, r9, #(1 << 25)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
ldr r9, [r2, #CCM_CBCDR]
|
||||
ldr r6, =0x3f1f00
|
||||
bic r9, r9, r6
|
||||
orr r9, r9, #(1 << 8)
|
||||
orr r9, r9, #(0x7 << 19)
|
||||
str r9, [r2, #CCM_CBCDR]
|
||||
|
||||
wait_for_ccm_handshake
|
||||
|
||||
.endm
|
||||
|
||||
.macro set_timings_below_100MHz_operation
|
||||
set_mmdc_misc_ralat_2_cycles
|
||||
|
||||
/* Adjust LPDDR2 timings for 24Mhz operation */
|
||||
ldr r5, =0x03162073
|
||||
str r5, [r8, #0xC] /* MMDC0_MDCFG0 */
|
||||
ldr r7, =0x00020482
|
||||
str r7, [r8, #0x10] /* MMDC0_MDCFG1 */
|
||||
ldr r9, =0x00000049
|
||||
str r9, [r8, #0x14] /* MMDC0_MDCFG2 */
|
||||
ldr r10, =0x00020333
|
||||
str r10, [r8, #0x38] /* MMDC0_MDCFG3LP */
|
||||
|
||||
/* Check if lpddr2 channel 1 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_below_100Mhz_ch1_timings
|
||||
|
||||
str r5, [r4, #0xC] /* MMDC1_MDCFG0 */
|
||||
str r7, [r4, #0x10] /* MMDC1_MDCFG1 */
|
||||
str r9, [r4, #0x14] /* MMDC1_MDCFG2 */
|
||||
str r10, [r4, #0x38] /* MMDC1_MDCFG3LP */
|
||||
|
||||
skip_below_100Mhz_ch1_timings:
|
||||
|
||||
.endm
|
||||
|
||||
.macro restore_mmdc_settings_info
|
||||
/* restore timing from mmdc_settings_info */
|
||||
ldr r6, [r1, #0x0]
|
||||
ldr r7, [r1, #0x4]
|
||||
1:
|
||||
ldr r9, [r7], #0x4
|
||||
ldr r10, [r7], #0x4
|
||||
str r10, [r8, r9]
|
||||
subs r6, r6, #0x1
|
||||
bne 1b
|
||||
|
||||
/* Check if lpddr2 channel 1 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq 3f
|
||||
|
||||
ldr r6, [r1, #0x0]
|
||||
ldr r7, [r1, #0x4]
|
||||
2:
|
||||
ldr r9, [r7], #0x4
|
||||
ldr r10, [r7], #0x4
|
||||
str r10, [r4, r9]
|
||||
subs r6, r6, #0x1
|
||||
bne 2b
|
||||
3:
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_clk_lower_equal_100MHz
|
||||
|
||||
ldr r10, =100000000
|
||||
cmp r0, r10
|
||||
beq set_timmings_100MHz
|
||||
set_timings_below_100MHz_operation
|
||||
b common_to_lower_equal_100MHz
|
||||
|
||||
set_timmings_100MHz:
|
||||
restore_mmdc_settings_info
|
||||
set_mmdc_misc_ralat_2_cycles
|
||||
|
||||
common_to_lower_equal_100MHz:
|
||||
|
||||
/* if MMDC is not in 400MHz mode, skip double mu count */
|
||||
ldr r5, [r1, #0x8]
|
||||
ldr r6, =400000000
|
||||
cmp r5, r6
|
||||
bne skip_lower_force_measure_ch1
|
||||
|
||||
/*
|
||||
* Prior to reducing the DDR frequency (at 528/400 MHz),
|
||||
* read the Measure unit count bits (MU_UNIT_DEL_NUM)
|
||||
*/
|
||||
ldr r5, =0x8B8
|
||||
ldr r6, [r8, r5]
|
||||
/* Original MU unit count */
|
||||
mov r6, r6, LSR #16
|
||||
ldr r9, =0x3FF
|
||||
and r6, r6, r9
|
||||
/* 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, [r8, r5]
|
||||
orr r6, r6, #0x400
|
||||
str r6, [r8, r5]
|
||||
/*
|
||||
* 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, [r8, r5]
|
||||
ldr r9, =0x3FF
|
||||
bic r6, r6, r9
|
||||
orr r6, r6, r7
|
||||
str r6, [r8, r5]
|
||||
/* Now perform a Force Measurement. */
|
||||
ldr r6, [r8, r5]
|
||||
orr r6, r6, #0x800
|
||||
str r6, [r8, r5]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
force_measure:
|
||||
ldr r6, [r8, r5]
|
||||
and r6, r6, #0x800
|
||||
cmp r6, #0x0
|
||||
bne force_measure
|
||||
|
||||
/* Check if lpddr2 channel 2 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_lower_force_measure_ch1
|
||||
|
||||
ldr r5, =0x8B8
|
||||
ldr r6, [r4, r5]
|
||||
/* Original MU unit count */
|
||||
mov r6, r6, LSR #16
|
||||
ldr r9, =0x3FF
|
||||
and r6, r6, r9
|
||||
/* 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, [r4, r5]
|
||||
orr r6, r6, #0x400
|
||||
str r6, [r4, r5]
|
||||
/*
|
||||
* 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, [r4, r5]
|
||||
ldr r9, =0x3FF
|
||||
bic r6, r6, r9
|
||||
orr r6, r6, r7
|
||||
str r6, [r4, r5]
|
||||
/* Now perform a Force Measurement. */
|
||||
ldr r6, [r4, r5]
|
||||
orr r6, r6, #0x800
|
||||
str r6, [r4, r5]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
force_measure_ch1:
|
||||
ldr r6, [r4, r5]
|
||||
and r6, r6, #0x800
|
||||
cmp r6, #0x0
|
||||
bne force_measure_ch1
|
||||
|
||||
skip_lower_force_measure_ch1:
|
||||
|
||||
.endm
|
||||
|
||||
.macro mmdc_clk_above_100MHz
|
||||
|
||||
restore_mmdc_settings_info
|
||||
|
||||
/* Make sure that the PHY measurement unit is NOT in bypass mode */
|
||||
ldr r5, =0x8B8
|
||||
ldr r6, [r8, r5]
|
||||
bic r6, r6, #0x400
|
||||
str r6, [r8, r5]
|
||||
/* Now perform a Force Measurement. */
|
||||
ldr r6, [r8, r5]
|
||||
orr r6, r6, #0x800
|
||||
str r6, [r8, r5]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
force_measure1:
|
||||
ldr r6, [r8, r5]
|
||||
and r6, r6, #0x800
|
||||
cmp r6, #0x0
|
||||
bne force_measure1
|
||||
|
||||
/* Check if lpddr2 channel 2 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_above_force_measure_ch1
|
||||
|
||||
ldr r5, =0x8B8
|
||||
ldr r6, [r4, r5]
|
||||
bic r6, r6, #0x400
|
||||
str r6, [r4, r5]
|
||||
/* Now perform a Force Measurement. */
|
||||
ldr r6, [r4, r5]
|
||||
orr r6, r6, #0x800
|
||||
str r6, [r4, r5]
|
||||
/* Wait for FRC_MSR to clear. */
|
||||
force_measure1_ch1:
|
||||
ldr r6, [r4, r5]
|
||||
and r6, r6, #0x800
|
||||
cmp r6, #0x0
|
||||
bne force_measure1_ch1
|
||||
|
||||
skip_above_force_measure_ch1:
|
||||
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r11, lr}
|
||||
|
||||
ldr r7, =v7_flush_kern_cache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r11, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
bic r6, r6, #0x4
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r11, lr}
|
||||
|
||||
ldr r7, =v7_flush_kern_cache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r11, lr}
|
||||
|
||||
.endm
|
||||
|
||||
/*
|
||||
* mx6_lpddr2_freq_change
|
||||
*
|
||||
* Make sure DDR is in self-refresh.
|
||||
* IRQs are already disabled.
|
||||
* r0 : DDR freq.
|
||||
* r1 : mmdc_settings_info
|
||||
*/
|
||||
.align 3
|
||||
ENTRY(mx6q_lpddr2_freq_change)
|
||||
mx6q_lpddr2_freq_change_start:
|
||||
push {r2-r10}
|
||||
|
||||
/*
|
||||
* Need to flush and disable L1 before
|
||||
* disabling L2, we need data to
|
||||
* coherent. Flushing L1 pushes
|
||||
* everyhting to L2. We sync L2 later, but
|
||||
* it can still have dirty lines.
|
||||
* While exiting, we need to enable L2 first
|
||||
* and then L1.
|
||||
*/
|
||||
disable_l1_dcache
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
#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)
|
||||
|
||||
/* Wait for background operations to complete. */
|
||||
wait_for_l2_to_idle:
|
||||
ldr r6, [r7, #0x730]
|
||||
cmp r6, #0x0
|
||||
bne wait_for_l2_to_idle
|
||||
|
||||
mov r6, #0x0
|
||||
str r6, [r7, #0x730]
|
||||
|
||||
/*
|
||||
* The second dsb might be needed to keep cache sync (device write)
|
||||
* ordering with the memory accesses before it.
|
||||
*/
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Disable L2. */
|
||||
str r6, [r7, #0x100]
|
||||
#endif
|
||||
|
||||
ldr r3, =IMX_IO_P2V(MX6Q_ANATOP_BASE_ADDR)
|
||||
ldr r2, =IMX_IO_P2V(MX6Q_CCM_BASE_ADDR)
|
||||
ldr r8, =IMX_IO_P2V(MX6Q_MMDC_P0_BASE_ADDR)
|
||||
ldr r4, =IMX_IO_P2V(MX6Q_MMDC_P1_BASE_ADDR)
|
||||
|
||||
/* Disable Automatic power savings. */
|
||||
ldr r6, [r8, #0x404]
|
||||
orr r6, r6, #0x01
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
/* MMDC0_MDPDC disable power down timer */
|
||||
ldr r6, [r8, #0x4]
|
||||
bic r6, r6, #0xff00
|
||||
str r6, [r8, #0x4]
|
||||
|
||||
/* Check if lpddr2 channel 2 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_psd_ch1
|
||||
|
||||
ldr r6, [r4, #0x404]
|
||||
orr r6, r6, #0x01
|
||||
str r6, [r4, #0x404]
|
||||
|
||||
ldr r6, [r4, #0x4]
|
||||
bic r6, r6, #0xff00
|
||||
str r6, [r4, #0x4]
|
||||
|
||||
skip_psd_ch1:
|
||||
/* Delay for a while */
|
||||
ldr r10, =10
|
||||
delay1:
|
||||
ldr r7, =0
|
||||
cont1:
|
||||
ldr r6, [r8, r7]
|
||||
add r7, r7, #4
|
||||
cmp r7, #16
|
||||
bne cont1
|
||||
sub r10, r10, #1
|
||||
cmp r10, #0
|
||||
bgt delay1
|
||||
|
||||
/* Make the DDR explicitly enter self-refresh. */
|
||||
ldr r6, [r8, #0x404]
|
||||
orr r6, r6, #0x200000
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
poll_dvfs_set_1:
|
||||
ldr r6, [r8, #0x404]
|
||||
and r6, r6, #0x2000000
|
||||
cmp r6, #0x2000000
|
||||
bne poll_dvfs_set_1
|
||||
|
||||
/* set SBS step-by-step mode */
|
||||
ldr r6, [r8, #0x410]
|
||||
orr r6, r6, #0x100
|
||||
str r6, [r8, #0x410]
|
||||
|
||||
/* Check if lpddr2 channel 2 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_sbs_ch1
|
||||
|
||||
ldr r6, [r4, #0x404]
|
||||
orr r6, r6, #0x200000
|
||||
str r6, [r4, #0x404]
|
||||
|
||||
poll_dvfs_set_2:
|
||||
ldr r6, [r4, #0x404]
|
||||
and r6, r6, #0x2000000
|
||||
cmp r6, #0x2000000
|
||||
bne poll_dvfs_set_2
|
||||
|
||||
ldr r6, [r4, #0x410]
|
||||
orr r6, r6, #0x100
|
||||
str r6, [r4, #0x410]
|
||||
|
||||
skip_sbs_ch1:
|
||||
ldr r10, =100000000
|
||||
cmp r0, r10
|
||||
bgt set_ddr_mu_above_100
|
||||
mmdc_clk_lower_equal_100MHz
|
||||
|
||||
set_ddr_mu_above_100:
|
||||
ldr r10, =24000000
|
||||
cmp r0, r10
|
||||
beq set_to_24MHz
|
||||
|
||||
ldr r10, =100000000
|
||||
cmp r0, r10
|
||||
beq set_to_100MHz
|
||||
|
||||
ldr r10, =400000000
|
||||
cmp r0, r10
|
||||
switch_to_400MHz
|
||||
b done
|
||||
|
||||
set_to_24MHz:
|
||||
/*
|
||||
switch_to_24MHZ_from_pll2
|
||||
*/
|
||||
switch_to_24MHz
|
||||
b done
|
||||
|
||||
set_to_100MHz:
|
||||
switch_to_100MHz
|
||||
|
||||
done:
|
||||
|
||||
ldr r10,=100000000
|
||||
cmp r0, r10
|
||||
ble skip_mmdc_clk_check
|
||||
mmdc_clk_above_100MHz
|
||||
|
||||
skip_mmdc_clk_check:
|
||||
|
||||
/* clear DVFS - exit from self refresh mode */
|
||||
ldr r6, [r8, #0x404]
|
||||
bic r6, r6, #0x200000
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
poll_dvfs_clear_1:
|
||||
ldr r6, [r8, #0x404]
|
||||
and r6, r6, #0x2000000
|
||||
cmp r6, #0x2000000
|
||||
beq poll_dvfs_clear_1
|
||||
|
||||
/* Enable Automatic power savings. */
|
||||
ldr r6, [r8, #0x404]
|
||||
bic r6, r6, #0x01
|
||||
str r6, [r8, #0x404]
|
||||
|
||||
/* Check if lpddr2 channel 2 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_enable_psd_ch1
|
||||
|
||||
ldr r6, [r4, #0x404]
|
||||
bic r6, r6, #0x200000
|
||||
str r6, [r4, #0x404]
|
||||
|
||||
poll_dvfs_clear_2:
|
||||
ldr r6, [r4, #0x404]
|
||||
and r6, r6, #0x2000000
|
||||
cmp r6, #0x2000000
|
||||
beq poll_dvfs_clear_2
|
||||
|
||||
ldr r6, [r4, #0x404]
|
||||
bic r6, r6, #0x01
|
||||
str r6, [r4, #0x404]
|
||||
|
||||
skip_enable_psd_ch1:
|
||||
ldr r10, =24000000
|
||||
cmp r0, r10
|
||||
beq skip_power_down
|
||||
|
||||
/* Enable MMDC power down timer. */
|
||||
ldr r6, [r8, #0x4]
|
||||
orr r6, r6, #0x5500
|
||||
str r6, [r8, #0x4]
|
||||
|
||||
/* Check if lpddr2 channel 2 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_power_down
|
||||
|
||||
ldr r6, [r4, #0x4]
|
||||
orr r6, r6, #0x5500
|
||||
str r6, [r4, #0x4]
|
||||
|
||||
skip_power_down:
|
||||
/* clear SBS - unblock DDR accesses */
|
||||
ldr r6, [r8, #0x410]
|
||||
bic r6, r6, #0x100
|
||||
str r6, [r8, #0x410]
|
||||
|
||||
/* Check if lpddr2 channel 2 is enabled */
|
||||
ldr r6, [r8, #0x18]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_disable_sbs_ch1
|
||||
|
||||
ldr r6, [r4, #0x410]
|
||||
bic r6, r6, #0x100
|
||||
str r6, [r4, #0x410]
|
||||
|
||||
skip_disable_sbs_ch1:
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* Enable L2. */
|
||||
ldr r7, =IMX_IO_P2V(MX6Q_L2_BASE_ADDR)
|
||||
ldr r6, =0x1
|
||||
str r6, [r7, #0x100]
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
|
||||
pop {r2-r10}
|
||||
|
||||
/* Restore registers */
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* Add ltorg here to ensure that all
|
||||
* literals are stored here and are
|
||||
* within the text space.
|
||||
*/
|
||||
.ltorg
|
||||
mx6q_lpddr2_freq_change_end:
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
|
||||
.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.MX6SLL, 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.MX6SLL, 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.MX6SLL when changing the
|
||||
* frequency of MMDC. the MMDC is the same on these two SOCs.
|
||||
*/
|
||||
ENTRY(imx6sll_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
|
||||
|
||||
#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
|
||||
|
||||
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]
|
||||
|
||||
/* 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]
|
||||
|
||||
/* clear SBS - unblock DDR accesses */
|
||||
ldr r6, [r5, #MMDC0_MADPCR0]
|
||||
bic r6, r6, #0x100
|
||||
str r6, [r5, #MMDC0_MADPCR0]
|
||||
|
||||
ldr r6, =0xa0000000
|
||||
str r6, [r5, #0x83c]
|
||||
|
||||
|
||||
#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
|
||||
|
||||
/* 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
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
* 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
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* Copyright (C) 2015-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 DDRC_MSTR 0x0
|
||||
#define DDRC_STAT 0x4
|
||||
#define DDRC_PWRCTL 0x30
|
||||
#define DDRC_RFSHTMG 0x64
|
||||
#define DDRC_DBG1 0x304
|
||||
#define DDRC_PSTAT 0x3fc
|
||||
#define DDRC_PCTRL_0 0x490
|
||||
#define DDRC_DFIMISC 0x1b0
|
||||
#define DDRC_DBGCAM 0x308
|
||||
#define DDRC_SWCTL 0x320
|
||||
#define DDRC_SWSTAT 0x324
|
||||
#define DDRPHY_LP_CON0 0x18
|
||||
#define IOMUXC_GPR8 0x20
|
||||
#define DDRPHY_PHY_CON1 0x4
|
||||
#define DDRPHY_MDLL_CON0 0xb0
|
||||
#define DDRPHY_MDLL_CON1 0xb4
|
||||
#define DDRPHY_OFFSETD_CON0 0x50
|
||||
#define DDRPHY_OFFSETR_CON0 0x20
|
||||
#define DDRPHY_OFFSETR_CON1 0x24
|
||||
#define DDRPHY_OFFSETR_CON2 0x28
|
||||
#define DDRPHY_OFFSETW_CON0 0x30
|
||||
#define DDRPHY_OFFSETW_CON1 0x34
|
||||
#define DDRPHY_OFFSETW_CON2 0x38
|
||||
#define DDRPHY_RFSHTMG 0x64
|
||||
#define DDRPHY_CA_WLDSKEW_CON0 0x6c
|
||||
#define DDRPHY_CA_DSKEW_CON0 0x7c
|
||||
#define DDRPHY_CA_DSKEW_CON1 0x80
|
||||
#define DDRPHY_CA_DSKEW_CON2 0x84
|
||||
|
||||
#define ANADIG_DIGPROG 0x800
|
||||
|
||||
.align 3
|
||||
|
||||
.macro ddrc_prepare
|
||||
|
||||
/* disable port */
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_PCTRL_0]
|
||||
|
||||
/* wait port busy done */
|
||||
ldr r6, =0x10001
|
||||
1:
|
||||
ldr r7, [r4, #DDRC_PSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, #0
|
||||
bne 1b
|
||||
|
||||
ldr r7, =0x20
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
ldr r6, =0x23
|
||||
2:
|
||||
ldr r7, [r4, #DDRC_STAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 2b
|
||||
|
||||
ldr r7, =0x1
|
||||
str r7, [r4, #DDRC_DBG1]
|
||||
|
||||
ldr r6, =0x30000000
|
||||
3:
|
||||
ldr r7, [r4, #DDRC_DBGCAM]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 3b
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_SWCTL]
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_DFIMISC]
|
||||
|
||||
ldr r7, =0x1
|
||||
str r7, [r4, #DDRC_SWCTL]
|
||||
|
||||
ldr r6, =0x1
|
||||
4:
|
||||
ldr r7, [r4, #DDRC_SWSTAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 4b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ddrc_done
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
ldr r6, =0x3
|
||||
5:
|
||||
ldr r7, [r4, #DDRC_STAT]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
beq 5b
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r4, #DDRC_DBG1]
|
||||
|
||||
ldr r7, =0x1
|
||||
str r7, [r4, #DDRC_PCTRL_0]
|
||||
|
||||
/* enable auto self-refresh */
|
||||
ldr r7, [r4, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 0)
|
||||
str r7, [r4, #DDRC_PWRCTL]
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_below_100m
|
||||
|
||||
/* LPDDR2 and LPDDR3 has different setting */
|
||||
ldr r8, [r4, #DDRC_MSTR]
|
||||
ands r8, r8, #0x4
|
||||
bne 9f
|
||||
|
||||
/* LPDDR3 */
|
||||
ldr r7, =0x00000100
|
||||
str r7, [r5, #DDRPHY_PHY_CON1]
|
||||
b 10f
|
||||
9:
|
||||
/* LPDDR2 */
|
||||
ldr r7, =0x10010100
|
||||
str r7, [r5, #DDRPHY_PHY_CON1]
|
||||
10:
|
||||
ldr r6, =24000000
|
||||
cmp r0, r6
|
||||
beq 16f
|
||||
|
||||
ldr r7, =0x0005000B
|
||||
str r7, [r4, #DDRC_RFSHTMG]
|
||||
b 6f
|
||||
16:
|
||||
ldr r7, =0x00010003
|
||||
str r7, [r4, #DDRC_RFSHTMG]
|
||||
|
||||
/* dram alt sel set to OSC */
|
||||
ldr r7, =0x10000000
|
||||
ldr r8, =0xa080
|
||||
str r7, [r2, r8]
|
||||
/* dram root set to from dram alt, div by 1 */
|
||||
ldr r7, =0x11000000
|
||||
ldr r8, =0x9880
|
||||
str r7, [r2, r8]
|
||||
b 7f
|
||||
|
||||
6:
|
||||
/* dram alt sel set to pfd0_392m */
|
||||
ldr r7, =0x15000000
|
||||
ldr r8, =0xa080
|
||||
str r7, [r2, r8]
|
||||
/* dram root set to from dram alt, div by 4 */
|
||||
ldr r7, =0x11000003
|
||||
ldr r8, =0x9880
|
||||
str r7, [r2, r8]
|
||||
7:
|
||||
ldr r7, =0x202ffd0
|
||||
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||
|
||||
ldr r7, =0x7f
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
ldr r7, =0x7f7f7f7f
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||
ldr r7, =0x7f
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||
|
||||
ldr r7, =0x7f7f7f7f
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||
ldr r7, =0x7f
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||
|
||||
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||
and r7, r7, #0x11
|
||||
cmp r7, #0x11
|
||||
bne 11f
|
||||
|
||||
ldr r7, =0x0
|
||||
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||
ldr r7, =0x60606060
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
ldr r7, =0x00006060
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
b 12f
|
||||
11:
|
||||
ldr r7, =0x0
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
12:
|
||||
ldr r7, =0x100007f
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
ldr r7, =0x7f
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
.endm
|
||||
|
||||
.macro switch_to_533m
|
||||
|
||||
ldr r7, =0x10210100
|
||||
str r7, [r5, #DDRPHY_PHY_CON1]
|
||||
|
||||
ldr r7, =0x00200038
|
||||
str r7, [r4, #DDRC_RFSHTMG]
|
||||
|
||||
/* dram root set to from dram main, div by 2 */
|
||||
ldr r7, =0x10000001
|
||||
ldr r8, =0x9880
|
||||
str r7, [r2, r8]
|
||||
|
||||
ldr r7, =0x1010007e
|
||||
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||
|
||||
ldr r7, =0x10000008
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
ldr r7, =0x08080808
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||
ldr r7, =0x8
|
||||
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||
|
||||
ldr r7, =0x08080808
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||
ldr r7, =0x8
|
||||
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||
|
||||
/* LPDDR2 and LPDDR3 has different setting */
|
||||
ldr r8, [r4, #DDRC_MSTR]
|
||||
ands r8, r8, #0x4
|
||||
beq 15f
|
||||
|
||||
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||
and r7, r7, #0x11
|
||||
cmp r7, #0x11
|
||||
bne 14f
|
||||
|
||||
ldr r7, =0x08080808
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
ldr r7, =0x0a0a0808
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
ldr r7, =0x0a0a0a0a
|
||||
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||
b 14f
|
||||
15:
|
||||
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||
and r7, r7, #0x11
|
||||
cmp r7, #0x11
|
||||
bne 13f
|
||||
|
||||
ldr r7, =0x1c1c1c1c
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
ldr r7, =0x30301c1c
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
ldr r7, =0x30303030
|
||||
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||
b 14f
|
||||
13:
|
||||
ldr r7, =0x08080808
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||
ldr r7, =0x0808
|
||||
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||
14:
|
||||
ldr r7, =0x11000008
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
ldr r7, =0x10000008
|
||||
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||
|
||||
ldr r6, =0x4
|
||||
8:
|
||||
ldr r7, [r5, #DDRPHY_MDLL_CON1]
|
||||
and r7, r7, r6
|
||||
cmp r7, r6
|
||||
bne 8b
|
||||
|
||||
.endm
|
||||
|
||||
ENTRY(imx_lpddr3_freq_change)
|
||||
push {r2 - r9}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
ldr r2, =IMX_IO_P2V(MX7D_CCM_BASE_ADDR)
|
||||
ldr r3, =IMX_IO_P2V(MX7D_IOMUXC_GPR_BASE_ADDR)
|
||||
ldr r4, =IMX_IO_P2V(MX7D_DDRC_BASE_ADDR)
|
||||
ldr r5, =IMX_IO_P2V(MX7D_DDRC_PHY_BASE_ADDR)
|
||||
ldr r9, =IMX_IO_P2V(MX7D_ANATOP_BASE_ADDR)
|
||||
|
||||
ddrc_prepare
|
||||
|
||||
ldr r6, =100000000
|
||||
cmp r0, r6
|
||||
bgt set_to_533m
|
||||
|
||||
set_to_below_100m:
|
||||
switch_to_below_100m
|
||||
b done
|
||||
|
||||
set_to_533m:
|
||||
switch_to_533m
|
||||
b done
|
||||
|
||||
done:
|
||||
ddrc_done
|
||||
|
||||
/* 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 - r9}
|
||||
mov pc, lr
|
||||
ENDPROC(imx_lpddr3_freq_change)
|
|
@ -106,6 +106,18 @@ static int ar8031_phy_fixup(struct phy_device *dev)
|
|||
{
|
||||
u16 val;
|
||||
|
||||
/* Set RGMII IO voltage to 1.8V */
|
||||
phy_write(dev, 0x1d, 0x1f);
|
||||
phy_write(dev, 0x1e, 0x8);
|
||||
|
||||
/* disable phy AR8031 SmartEEE function. */
|
||||
phy_write(dev, 0xd, 0x3);
|
||||
phy_write(dev, 0xe, 0x805d);
|
||||
phy_write(dev, 0xd, 0x4003);
|
||||
val = phy_read(dev, 0xe);
|
||||
val &= ~(0x1 << 8);
|
||||
phy_write(dev, 0xe, val);
|
||||
|
||||
/* To enable AR8031 output a 125MHz clk from CLK_25M */
|
||||
phy_write(dev, 0xd, 0x7);
|
||||
phy_write(dev, 0xe, 0x8016);
|
||||
|
@ -223,6 +235,58 @@ put_node:
|
|||
of_node_put(np);
|
||||
}
|
||||
|
||||
static void __init imx6q_csi_mux_init(void)
|
||||
{
|
||||
/*
|
||||
* MX6Q SabreSD board:
|
||||
* IPU1 CSI0 connects to parallel interface.
|
||||
* Set GPR1 bit 19 to 0x1.
|
||||
*
|
||||
* MX6DL SabreSD board:
|
||||
* IPU1 CSI0 connects to parallel interface.
|
||||
* Set GPR13 bit 0-2 to 0x4.
|
||||
* IPU1 CSI1 connects to MIPI CSI2 virtual channel 1.
|
||||
* Set GPR13 bit 3-5 to 0x1.
|
||||
*/
|
||||
struct regmap *gpr;
|
||||
|
||||
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
|
||||
if (!IS_ERR(gpr)) {
|
||||
if (of_machine_is_compatible("fsl,imx6q-sabresd") ||
|
||||
of_machine_is_compatible("fsl,imx6q-sabreauto") ||
|
||||
of_machine_is_compatible("fsl,imx6qp-sabresd") ||
|
||||
of_machine_is_compatible("fsl,imx6qp-sabreauto"))
|
||||
regmap_update_bits(gpr, IOMUXC_GPR1, 1 << 19, 1 << 19);
|
||||
else if (of_machine_is_compatible("fsl,imx6dl-sabresd") ||
|
||||
of_machine_is_compatible("fsl,imx6dl-sabreauto"))
|
||||
regmap_update_bits(gpr, IOMUXC_GPR13, 0x3F, 0x0C);
|
||||
} else {
|
||||
pr_err("%s(): failed to find fsl,imx6q-iomux-gpr regmap\n",
|
||||
__func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init imx6q_enet_clk_sel(void)
|
||||
{
|
||||
struct regmap *gpr;
|
||||
|
||||
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
|
||||
if (!IS_ERR(gpr))
|
||||
regmap_update_bits(gpr, IOMUXC_GPR5,
|
||||
IMX6Q_GPR5_ENET_TX_CLK_SEL, IMX6Q_GPR5_ENET_TX_CLK_SEL);
|
||||
else
|
||||
pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n");
|
||||
}
|
||||
|
||||
static inline void imx6q_enet_init(void)
|
||||
{
|
||||
imx6_enet_mac_init("fsl,imx6q-fec", "fsl,imx6q-ocotp");
|
||||
imx6q_enet_phy_init();
|
||||
imx6q_1588_init();
|
||||
if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0)
|
||||
imx6q_enet_clk_sel();
|
||||
}
|
||||
|
||||
static void __init imx6q_axi_init(void)
|
||||
{
|
||||
struct regmap *gpr;
|
||||
|
@ -270,13 +334,12 @@ static void __init imx6q_init_machine(void)
|
|||
if (parent == NULL)
|
||||
pr_warn("failed to initialize soc device\n");
|
||||
|
||||
imx6q_enet_phy_init();
|
||||
|
||||
of_platform_default_populate(NULL, NULL, parent);
|
||||
|
||||
imx_anatop_init();
|
||||
imx6q_enet_init();
|
||||
imx6q_csi_mux_init();
|
||||
cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init();
|
||||
imx6q_1588_init();
|
||||
imx6q_axi_init();
|
||||
}
|
||||
|
||||
|
@ -300,6 +363,8 @@ static void __init imx6q_map_io(void)
|
|||
{
|
||||
debug_ll_io_init();
|
||||
imx_scu_map_io();
|
||||
imx6_pm_map_io();
|
||||
imx_busfreq_map_io();
|
||||
}
|
||||
|
||||
static void __init imx6q_init_irq(void)
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "cpuidle.h"
|
||||
#include "hardware.h"
|
||||
|
||||
static void __init imx6sl_fec_init(void)
|
||||
static void __init imx6sl_fec_clk_init(void)
|
||||
{
|
||||
struct regmap *gpr;
|
||||
|
||||
|
@ -32,6 +32,12 @@ static void __init imx6sl_fec_init(void)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void imx6sl_fec_init(void)
|
||||
{
|
||||
imx6sl_fec_clk_init();
|
||||
imx6_enet_mac_init("fsl,imx6sl-fec", "fsl,imx6sl-ocotp");
|
||||
}
|
||||
|
||||
static void __init imx6sl_init_late(void)
|
||||
{
|
||||
/* imx6sl reuses imx6q cpufreq driver */
|
||||
|
@ -41,7 +47,7 @@ static void __init imx6sl_init_late(void)
|
|||
if (IS_ENABLED(CONFIG_SOC_IMX6SL) && cpu_is_imx6sl())
|
||||
imx6sl_cpuidle_init();
|
||||
else if (IS_ENABLED(CONFIG_SOC_IMX6SLL))
|
||||
imx6sx_cpuidle_init();
|
||||
imx6sll_cpuidle_init();
|
||||
}
|
||||
|
||||
static void __init imx6sl_init_machine(void)
|
||||
|
@ -54,6 +60,7 @@ static void __init imx6sl_init_machine(void)
|
|||
|
||||
of_platform_default_populate(NULL, NULL, parent);
|
||||
|
||||
imx_anatop_init();
|
||||
if (cpu_is_imx6sl())
|
||||
imx6sl_fec_init();
|
||||
imx_anatop_init();
|
||||
|
@ -73,6 +80,14 @@ static void __init imx6sl_init_irq(void)
|
|||
imx6_pm_ccm_init("fsl,imx6sll-ccm");
|
||||
}
|
||||
|
||||
static void __init imx6sl_map_io(void)
|
||||
{
|
||||
imx6_pm_map_io();
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
imx_busfreq_map_io();
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char * const imx6sl_dt_compat[] __initconst = {
|
||||
"fsl,imx6sl",
|
||||
"fsl,imx6sll",
|
||||
|
@ -82,6 +97,7 @@ static const char * const imx6sl_dt_compat[] __initconst = {
|
|||
DT_MACHINE_START(IMX6SL, "Freescale i.MX6 SoloLite (Device Tree)")
|
||||
.l2c_aux_val = 0,
|
||||
.l2c_aux_mask = ~0,
|
||||
.map_io = imx6sl_map_io,
|
||||
.init_irq = imx6sl_init_irq,
|
||||
.init_machine = imx6sl_init_machine,
|
||||
.init_late = imx6sl_init_late,
|
||||
|
|
|
@ -23,6 +23,14 @@ static int ar8031_phy_fixup(struct phy_device *dev)
|
|||
phy_write(dev, 0x1d, 0x1f);
|
||||
phy_write(dev, 0x1e, 0x8);
|
||||
|
||||
/* disable phy AR8031 SmartEEE function. */
|
||||
phy_write(dev, 0xd, 0x3);
|
||||
phy_write(dev, 0xe, 0x805d);
|
||||
phy_write(dev, 0xd, 0x4003);
|
||||
val = phy_read(dev, 0xe);
|
||||
val &= ~(0x1 << 8);
|
||||
phy_write(dev, 0xe, val);
|
||||
|
||||
/* introduce tx clock delay */
|
||||
phy_write(dev, 0x1d, 0x5);
|
||||
val = phy_read(dev, 0x1e);
|
||||
|
@ -57,6 +65,7 @@ static void __init imx6sx_enet_clk_sel(void)
|
|||
|
||||
static inline void imx6sx_enet_init(void)
|
||||
{
|
||||
imx6_enet_mac_init("fsl,imx6sx-fec", "fsl,imx6sx-ocotp");
|
||||
imx6sx_enet_phy_init();
|
||||
imx6sx_enet_clk_sel();
|
||||
}
|
||||
|
@ -71,6 +80,7 @@ static void __init imx6sx_init_machine(void)
|
|||
|
||||
of_platform_default_populate(NULL, NULL, parent);
|
||||
|
||||
imx_anatop_init();
|
||||
imx6sx_enet_init();
|
||||
imx_anatop_init();
|
||||
imx6sx_pm_init();
|
||||
|
@ -86,6 +96,13 @@ static void __init imx6sx_init_irq(void)
|
|||
imx6_pm_ccm_init("fsl,imx6sx-ccm");
|
||||
}
|
||||
|
||||
static void __init imx6sx_map_io(void)
|
||||
{
|
||||
debug_ll_io_init();
|
||||
imx6_pm_map_io();
|
||||
imx_busfreq_map_io();
|
||||
}
|
||||
|
||||
static void __init imx6sx_init_late(void)
|
||||
{
|
||||
imx6sx_cpuidle_init();
|
||||
|
@ -102,6 +119,7 @@ static const char * const imx6sx_dt_compat[] __initconst = {
|
|||
DT_MACHINE_START(IMX6SX, "Freescale i.MX6 SoloX (Device Tree)")
|
||||
.l2c_aux_val = 0,
|
||||
.l2c_aux_mask = ~0,
|
||||
.map_io = imx6sx_map_io,
|
||||
.init_irq = imx6sx_init_irq,
|
||||
.init_machine = imx6sx_init_machine,
|
||||
.dt_compat = imx6sx_dt_compat,
|
||||
|
|
|
@ -52,6 +52,7 @@ static inline void imx6ul_enet_init(void)
|
|||
{
|
||||
imx6ul_enet_clk_init();
|
||||
imx6ul_enet_phy_init();
|
||||
imx6_enet_mac_init("fsl,imx6ul-fec", "fsl,imx6ul-ocotp");
|
||||
}
|
||||
|
||||
static void __init imx6ul_init_machine(void)
|
||||
|
@ -63,6 +64,7 @@ static void __init imx6ul_init_machine(void)
|
|||
pr_warn("failed to initialize soc device\n");
|
||||
|
||||
of_platform_default_populate(NULL, NULL, parent);
|
||||
imx_anatop_init();
|
||||
imx6ul_enet_init();
|
||||
imx_anatop_init();
|
||||
imx6ul_pm_init();
|
||||
|
@ -78,12 +80,18 @@ static void __init imx6ul_init_irq(void)
|
|||
|
||||
static void __init imx6ul_init_late(void)
|
||||
{
|
||||
imx6sx_cpuidle_init();
|
||||
imx6ul_cpuidle_init();
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
|
||||
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
|
||||
}
|
||||
|
||||
static void __init imx6ul_map_io(void)
|
||||
{
|
||||
imx6_pm_map_io();
|
||||
imx_busfreq_map_io();
|
||||
}
|
||||
|
||||
static const char * const imx6ul_dt_compat[] __initconst = {
|
||||
"fsl,imx6ul",
|
||||
"fsl,imx6ull",
|
||||
|
@ -91,6 +99,7 @@ static const char * const imx6ul_dt_compat[] __initconst = {
|
|||
};
|
||||
|
||||
DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
|
||||
.map_io = imx6ul_map_io,
|
||||
.init_irq = imx6ul_init_irq,
|
||||
.init_machine = imx6ul_init_machine,
|
||||
.init_late = imx6ul_init_late,
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
#include <asm/mach/map.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
|
||||
static struct property device_disabled = {
|
||||
.name = "status",
|
||||
.length = sizeof("disabled"),
|
||||
.value = "disabled",
|
||||
};
|
||||
|
||||
static int ar8031_phy_fixup(struct phy_device *dev)
|
||||
{
|
||||
|
@ -57,6 +64,23 @@ static void __init imx7d_enet_phy_init(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void __init imx7d_enet_mdio_fixup(void)
|
||||
{
|
||||
struct regmap *gpr;
|
||||
|
||||
/* The management data input/output (MDIO) bus where often high-speed,
|
||||
* open-drain operation is required. i.MX7D TO1.0 ENET MDIO pin has no
|
||||
* open drain as IC ticket number: TKT252980, i.MX7D TO1.1 fix the issue.
|
||||
* GPR1[8:7] are reserved bits at TO1.0, there no need to add version check.
|
||||
*/
|
||||
gpr = syscon_regmap_lookup_by_compatible("fsl,imx7d-iomuxc-gpr");
|
||||
if (!IS_ERR(gpr))
|
||||
regmap_update_bits(gpr, IOMUXC_GPR0, IMX7D_GPR0_ENET_MDIO_OPEN_DRAIN_MASK,
|
||||
IMX7D_GPR0_ENET_MDIO_OPEN_DRAIN_MASK);
|
||||
else
|
||||
pr_err("failed to find fsl,imx7d-iomux-gpr regmap\n");
|
||||
}
|
||||
|
||||
static void __init imx7d_enet_clk_sel(void)
|
||||
{
|
||||
struct regmap *gpr;
|
||||
|
@ -72,10 +96,23 @@ static void __init imx7d_enet_clk_sel(void)
|
|||
|
||||
static inline void imx7d_enet_init(void)
|
||||
{
|
||||
imx6_enet_mac_init("fsl,imx7d-fec", "fsl,imx7d-ocotp");
|
||||
imx7d_enet_mdio_fixup();
|
||||
imx7d_enet_phy_init();
|
||||
imx7d_enet_clk_sel();
|
||||
}
|
||||
|
||||
static inline void imx7d_disable_arm_arch_timer(void)
|
||||
{
|
||||
struct device_node *node;
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "arm,armv7-timer");
|
||||
if (node) {
|
||||
pr_info("disable arm arch timer for nosmp!\n");
|
||||
of_add_property(node, &device_disabled);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init imx7d_init_machine(void)
|
||||
{
|
||||
struct device *parent;
|
||||
|
@ -84,21 +121,36 @@ static void __init imx7d_init_machine(void)
|
|||
if (parent == NULL)
|
||||
pr_warn("failed to initialize soc device\n");
|
||||
|
||||
imx_anatop_init();
|
||||
of_platform_default_populate(NULL, NULL, parent);
|
||||
imx7d_pm_init();
|
||||
imx_anatop_init();
|
||||
imx7d_enet_init();
|
||||
}
|
||||
|
||||
static void __init imx7d_init_late(void)
|
||||
{
|
||||
imx7d_cpuidle_init();
|
||||
if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT))
|
||||
platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0);
|
||||
}
|
||||
|
||||
static void __init imx7d_init_irq(void)
|
||||
{
|
||||
imx_gpcv2_check_dt();
|
||||
imx_init_revision_from_anatop();
|
||||
imx_src_init();
|
||||
irqchip_init();
|
||||
#ifndef CONFIG_SMP
|
||||
imx7d_disable_arm_arch_timer();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void __init imx7d_map_io(void)
|
||||
{
|
||||
debug_ll_io_init();
|
||||
imx7_pm_map_io();
|
||||
imx_busfreq_map_io();
|
||||
}
|
||||
|
||||
static const char *const imx7d_dt_compat[] __initconst = {
|
||||
|
@ -108,6 +160,8 @@ static const char *const imx7d_dt_compat[] __initconst = {
|
|||
};
|
||||
|
||||
DT_MACHINE_START(IMX7D, "Freescale i.MX7 Dual (Device Tree)")
|
||||
.map_io = imx7d_map_io,
|
||||
.smp = smp_ops(imx_smp_ops),
|
||||
.init_irq = imx7d_init_irq,
|
||||
.init_machine = imx7d_init_machine,
|
||||
.init_late = imx7d_init_late,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
|
@ -17,6 +18,15 @@
|
|||
|
||||
#define SIM_JTAG_ID_REG 0x8c
|
||||
|
||||
/* static IO mapping, and ioremap() could always share the same mapping. */
|
||||
static struct map_desc mx7ulp_io_desc[] __initdata = {
|
||||
mx7ulp_aips_map_entry(1, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(2, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(3, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(4, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(5, MT_DEVICE),
|
||||
};
|
||||
|
||||
static void __init imx7ulp_set_revision(void)
|
||||
{
|
||||
struct regmap *sim;
|
||||
|
@ -65,12 +75,23 @@ static const char *const imx7ulp_dt_compat[] __initconst = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static void __init imx7ulp_map_io(void)
|
||||
{
|
||||
iotable_init(mx7ulp_io_desc, ARRAY_SIZE(mx7ulp_io_desc));
|
||||
imx7ulp_pm_map_io();
|
||||
}
|
||||
|
||||
static void __init imx7ulp_init_late(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARM_IMX7ULP_CPUFREQ))
|
||||
platform_device_register_simple("imx7ulp-cpufreq", -1, NULL, 0);
|
||||
|
||||
imx7ulp_cpuidle_init();
|
||||
imx7ulp_enable_nmi();
|
||||
}
|
||||
|
||||
DT_MACHINE_START(IMX7ulp, "Freescale i.MX7ULP (Device Tree)")
|
||||
.map_io = imx7ulp_map_io,
|
||||
.init_machine = imx7ulp_init_machine,
|
||||
.dt_compat = imx7ulp_dt_compat,
|
||||
.init_late = imx7ulp_init_late,
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#define to_mmdc_pmu(p) container_of(p, struct mmdc_pmu, pmu)
|
||||
|
||||
static int ddr_type;
|
||||
static int lpddr2_2ch_mode;
|
||||
|
||||
struct fsl_mmdc_devtype_data {
|
||||
unsigned int flags;
|
||||
|
@ -575,6 +576,11 @@ int imx_mmdc_get_ddr_type(void)
|
|||
return ddr_type;
|
||||
}
|
||||
|
||||
int imx_mmdc_get_lpddr2_2ch_mode(void)
|
||||
{
|
||||
return lpddr2_2ch_mode;
|
||||
}
|
||||
|
||||
static struct platform_driver imx_mmdc_driver = {
|
||||
.driver = {
|
||||
.name = "imx-mmdc",
|
||||
|
|
|
@ -0,0 +1,434 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/busfreq-imx.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "common.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define MU_ATR0_OFFSET 0x0
|
||||
#define MU_ARR0_OFFSET 0x10
|
||||
#define MU_ARR1_OFFSET 0x14
|
||||
#define MU_ASR 0x20
|
||||
#define MU_ACR 0x24
|
||||
#define MX7ULP_MU_TR0 0x20
|
||||
#define MX7ULP_MU_RR0 0x40
|
||||
#define MX7ULP_MU_RR1 0x44
|
||||
#define MX7ULP_MU_SR 0x60
|
||||
#define MX7ULP_MU_CR 0x64
|
||||
|
||||
#define MU_LPM_HANDSHAKE_INDEX 0
|
||||
#define MU_RPMSG_HANDSHAKE_INDEX 1
|
||||
#define MU_LPM_BUS_HIGH_READY_FOR_M4 0xFFFF6666
|
||||
#define MU_LPM_M4_FREQ_CHANGE_READY 0xFFFF7777
|
||||
#define MU_LPM_M4_REQUEST_HIGH_BUS 0x2222CCCC
|
||||
#define MU_LPM_M4_RELEASE_HIGH_BUS 0x2222BBBB
|
||||
#define MU_LPM_M4_WAKEUP_SRC_VAL 0x55555000
|
||||
#define MU_LPM_M4_WAKEUP_SRC_MASK 0xFFFFF000
|
||||
#define MU_LPM_M4_WAKEUP_IRQ_MASK 0xFF0
|
||||
#define MU_LPM_M4_WAKEUP_IRQ_SHIFT 0x4
|
||||
#define MU_LPM_M4_WAKEUP_ENABLE_MASK 0xF
|
||||
#define MU_LPM_M4_WAKEUP_ENABLE_SHIFT 0x0
|
||||
|
||||
#define MU_LPM_M4_RUN_MODE 0x5A5A0001
|
||||
#define MU_LPM_M4_WAIT_MODE 0x5A5A0002
|
||||
#define MU_LPM_M4_STOP_MODE 0x5A5A0003
|
||||
|
||||
#define MAX_NUM 10 /* enlarge it if overflow happen */
|
||||
|
||||
static void __iomem *mu_base;
|
||||
static u32 m4_message[MAX_NUM];
|
||||
static u32 in_idx, out_idx;
|
||||
static struct delayed_work mu_work;
|
||||
static u32 m4_wake_irqs[4];
|
||||
static bool m4_freq_low;
|
||||
struct irq_domain *domain;
|
||||
static bool m4_in_stop;
|
||||
static struct clk *clk;
|
||||
static DEFINE_SPINLOCK(mu_lock);
|
||||
|
||||
void imx_mu_set_m4_run_mode(void)
|
||||
{
|
||||
m4_in_stop = false;
|
||||
}
|
||||
|
||||
bool imx_mu_is_m4_in_stop(void)
|
||||
{
|
||||
return m4_in_stop;
|
||||
}
|
||||
|
||||
bool imx_mu_is_m4_in_low_freq(void)
|
||||
{
|
||||
return m4_freq_low;
|
||||
}
|
||||
|
||||
void imx_mu_enable_m4_irqs_in_gic(bool enable)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (m4_wake_irqs[i] == 0)
|
||||
continue;
|
||||
for (j = 0; j < 32; j++) {
|
||||
if (m4_wake_irqs[i] & (1 << j)) {
|
||||
if (enable)
|
||||
enable_irq(irq_find_mapping(
|
||||
domain, i * 32 + j));
|
||||
else
|
||||
disable_irq(irq_find_mapping(
|
||||
domain, i * 32 + j));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t mcc_m4_dummy_isr(int irq, void *param)
|
||||
{
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int imx_mu_send_message(unsigned int index, unsigned int data)
|
||||
{
|
||||
u32 val, ep;
|
||||
int i, te_flag = 0;
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(500);
|
||||
|
||||
/* wait for transfer buffer empty, and no event pending */
|
||||
do {
|
||||
if (cpu_is_imx7ulp())
|
||||
val = readl_relaxed(mu_base + MX7ULP_MU_SR);
|
||||
else
|
||||
val = readl_relaxed(mu_base + MU_ASR);
|
||||
ep = val & BIT(4);
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("Waiting MU transmit buffer empty timeout!\n");
|
||||
return -EIO;
|
||||
}
|
||||
} while (((val & (1 << (20 + 3 - index))) == 0) || (ep == BIT(4)));
|
||||
|
||||
if (cpu_is_imx7ulp())
|
||||
writel_relaxed(data, mu_base + index * 0x4 + MX7ULP_MU_TR0);
|
||||
else
|
||||
writel_relaxed(data, mu_base + index * 0x4 + MU_ATR0_OFFSET);
|
||||
|
||||
/*
|
||||
* make a double check that TEn is not empty after write
|
||||
*/
|
||||
if (cpu_is_imx7ulp())
|
||||
val = readl_relaxed(mu_base + MX7ULP_MU_SR);
|
||||
else
|
||||
val = readl_relaxed(mu_base + MU_ASR);
|
||||
ep = val & BIT(4);
|
||||
if (((val & (1 << (20 + (3 - index)))) == 0) || (ep == BIT(4)))
|
||||
return 0;
|
||||
else
|
||||
te_flag = 1;
|
||||
|
||||
/*
|
||||
* Make sure that TEn flag is changed, after the ATRn is filled up.
|
||||
*/
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (cpu_is_imx7ulp())
|
||||
val = readl_relaxed(mu_base + MX7ULP_MU_SR);
|
||||
else
|
||||
val = readl_relaxed(mu_base + MU_ASR);
|
||||
ep = val & BIT(4);
|
||||
if (((val & (1 << (20 + 3 - index))) == 0) || (ep == BIT(4))) {
|
||||
/*
|
||||
* BUG here. TEn flag is changes, after the
|
||||
* ATRn is filled with MSG for a while.
|
||||
*/
|
||||
te_flag = 0;
|
||||
break;
|
||||
} else if (time_after(jiffies, timeout)) {
|
||||
/* Can't see TEn 1->0, maybe already handled! */
|
||||
te_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (te_flag == 0)
|
||||
pr_info("BUG: TEn is not changed immediately"
|
||||
"when ATRn is filled up.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mu_work_handler(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
u32 irq, enable, idx, mask, virq;
|
||||
struct of_phandle_args args;
|
||||
u32 message;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mu_lock, flags);
|
||||
message = m4_message[out_idx % MAX_NUM];
|
||||
spin_unlock_irqrestore(&mu_lock, flags);
|
||||
|
||||
pr_debug("receive M4 message 0x%x\n", message);
|
||||
|
||||
switch (message) {
|
||||
case MU_LPM_M4_RUN_MODE:
|
||||
case MU_LPM_M4_WAIT_MODE:
|
||||
m4_in_stop = false;
|
||||
break;
|
||||
case MU_LPM_M4_STOP_MODE:
|
||||
m4_in_stop = true;
|
||||
break;
|
||||
case MU_LPM_M4_REQUEST_HIGH_BUS:
|
||||
request_bus_freq(BUS_FREQ_HIGH);
|
||||
#ifdef CONFIG_SOC_IMX6SX
|
||||
if (cpu_is_imx6sx())
|
||||
imx6sx_set_m4_highfreq(true);
|
||||
#endif
|
||||
imx_mu_send_message(MU_LPM_HANDSHAKE_INDEX,
|
||||
MU_LPM_BUS_HIGH_READY_FOR_M4);
|
||||
m4_freq_low = false;
|
||||
break;
|
||||
case MU_LPM_M4_RELEASE_HIGH_BUS:
|
||||
release_bus_freq(BUS_FREQ_HIGH);
|
||||
#ifdef CONFIG_SOC_IMX6SX
|
||||
if (cpu_is_imx6sx()) {
|
||||
imx6sx_set_m4_highfreq(false);
|
||||
imx_mu_send_message(MU_LPM_HANDSHAKE_INDEX,
|
||||
MU_LPM_M4_FREQ_CHANGE_READY);
|
||||
}
|
||||
#endif
|
||||
m4_freq_low = true;
|
||||
break;
|
||||
default:
|
||||
if ((message & MU_LPM_M4_WAKEUP_SRC_MASK) ==
|
||||
MU_LPM_M4_WAKEUP_SRC_VAL) {
|
||||
irq = (message & MU_LPM_M4_WAKEUP_IRQ_MASK) >>
|
||||
MU_LPM_M4_WAKEUP_IRQ_SHIFT;
|
||||
|
||||
enable = (message & MU_LPM_M4_WAKEUP_ENABLE_MASK) >>
|
||||
MU_LPM_M4_WAKEUP_ENABLE_SHIFT;
|
||||
|
||||
/* to hwirq start from 0 */
|
||||
irq -= 32;
|
||||
|
||||
idx = irq / 32;
|
||||
mask = 1 << irq % 32;
|
||||
|
||||
args.np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-gpc");
|
||||
args.args_count = 3;
|
||||
args.args[0] = 0;
|
||||
args.args[1] = irq;
|
||||
args.args[2] = IRQ_TYPE_LEVEL_HIGH;
|
||||
|
||||
virq = irq_create_of_mapping(&args);
|
||||
|
||||
if (enable && can_request_irq(virq, 0)) {
|
||||
ret = request_irq(virq, mcc_m4_dummy_isr,
|
||||
IRQF_NO_SUSPEND, "imx-m4-dummy", NULL);
|
||||
if (ret) {
|
||||
pr_err("%s: register interrupt %d failed, rc %d\n",
|
||||
__func__, virq, ret);
|
||||
break;
|
||||
}
|
||||
disable_irq(virq);
|
||||
m4_wake_irqs[idx] = m4_wake_irqs[idx] | mask;
|
||||
}
|
||||
imx_gpc_add_m4_wake_up_irq(irq, enable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&mu_lock, flags);
|
||||
m4_message[out_idx % MAX_NUM] = 0;
|
||||
out_idx++;
|
||||
spin_unlock_irqrestore(&mu_lock, flags);
|
||||
|
||||
/* enable RIE3 interrupt */
|
||||
if (cpu_is_imx7ulp())
|
||||
writel_relaxed(readl_relaxed(mu_base + MX7ULP_MU_CR) | BIT(27),
|
||||
mu_base + MX7ULP_MU_CR);
|
||||
else
|
||||
writel_relaxed(readl_relaxed(mu_base + MU_ACR) | BIT(27),
|
||||
mu_base + MU_ACR);
|
||||
}
|
||||
|
||||
int imx_mu_lpm_ready(bool ready)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (cpu_is_imx7ulp()) {
|
||||
val = readl_relaxed(mu_base + MX7ULP_MU_CR);
|
||||
if (ready)
|
||||
writel_relaxed(val | BIT(0), mu_base + MX7ULP_MU_CR);
|
||||
else
|
||||
writel_relaxed(val & ~BIT(0), mu_base + MX7ULP_MU_CR);
|
||||
} else {
|
||||
val = readl_relaxed(mu_base + MU_ACR);
|
||||
if (ready)
|
||||
writel_relaxed(val | BIT(0), mu_base + MU_ACR);
|
||||
else
|
||||
writel_relaxed(val & ~BIT(0), mu_base + MU_ACR);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t imx_mu_isr(int irq, void *param)
|
||||
{
|
||||
u32 irqs;
|
||||
unsigned long flags;
|
||||
|
||||
if (cpu_is_imx7ulp())
|
||||
irqs = readl_relaxed(mu_base + MX7ULP_MU_SR);
|
||||
else
|
||||
irqs = readl_relaxed(mu_base + MU_ASR);
|
||||
|
||||
if (irqs & (1 << 27)) {
|
||||
spin_lock_irqsave(&mu_lock, flags);
|
||||
/* get message from receive buffer */
|
||||
if (cpu_is_imx7ulp())
|
||||
m4_message[in_idx % MAX_NUM] = readl_relaxed(mu_base +
|
||||
MX7ULP_MU_RR0);
|
||||
else
|
||||
m4_message[in_idx % MAX_NUM] = readl_relaxed(mu_base +
|
||||
MU_ARR0_OFFSET);
|
||||
/* disable RIE3 interrupt */
|
||||
if (cpu_is_imx7ulp())
|
||||
writel_relaxed(readl_relaxed(mu_base + MX7ULP_MU_CR)
|
||||
& (~BIT(27)), mu_base + MX7ULP_MU_CR);
|
||||
else
|
||||
writel_relaxed(readl_relaxed(mu_base + MU_ACR)
|
||||
& (~BIT(27)), mu_base + MU_ACR);
|
||||
in_idx++;
|
||||
if (in_idx == out_idx) {
|
||||
spin_unlock_irqrestore(&mu_lock, flags);
|
||||
pr_err("MU overflow!\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&mu_lock, flags);
|
||||
|
||||
schedule_delayed_work(&mu_work, 0);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int imx_mu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
u32 irq;
|
||||
struct device_node *np;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-mu-lp");
|
||||
mu_base = of_iomap(np, 0);
|
||||
WARN_ON(!mu_base);
|
||||
|
||||
ret = of_device_is_compatible(np, "fsl,imx7ulp-mu-lp");
|
||||
if (ret)
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
else
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = request_irq(irq, imx_mu_isr,
|
||||
IRQF_NO_SUSPEND | IRQF_SHARED, "imx-mu-lp", dev);
|
||||
if (ret) {
|
||||
pr_err("%s: register interrupt %d failed, rc %d\n",
|
||||
__func__, irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_device_is_compatible(np, "fsl,imx7d-mu-lp");
|
||||
if (ret) {
|
||||
clk = devm_clk_get(&pdev->dev, "mu");
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"mu clock source missing or invalid\n");
|
||||
return PTR_ERR(clk);
|
||||
} else {
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to enable mu clock\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* MU always as a wakeup source for low power mode */
|
||||
imx_gpcv2_add_m4_wake_up_irq(irq_to_desc(irq)->irq_data.hwirq,
|
||||
true);
|
||||
} else {
|
||||
/* MU always as a wakeup source for low power mode */
|
||||
imx_gpc_add_m4_wake_up_irq(irq_to_desc(irq)->irq_data.hwirq, true);
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&mu_work, mu_work_handler);
|
||||
/* bit0 of MX7ULP_MU_CR used to let m4 to know MU is ready now */
|
||||
if (cpu_is_imx7ulp())
|
||||
writel_relaxed(readl_relaxed(mu_base + MX7ULP_MU_CR) |
|
||||
BIT(0) | BIT(26) | BIT(27), mu_base + MX7ULP_MU_CR);
|
||||
else
|
||||
writel_relaxed(readl_relaxed(mu_base + MU_ACR) |
|
||||
BIT(26) | BIT(27), mu_base + MU_ACR);
|
||||
|
||||
pr_info("MU is ready for cross core communication!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_mu_ids[] = {
|
||||
{ .compatible = "fsl,imx6sx-mu-lp" },
|
||||
{ .compatible = "fsl,imx7d-mu-lp" },
|
||||
{ .compatible = "fsl,imx7ulp-mu-lp" },
|
||||
{ }
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mu_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mu_resume(struct device *dev)
|
||||
{
|
||||
if (!cpu_is_imx7ulp())
|
||||
return 0;
|
||||
|
||||
writel_relaxed(readl_relaxed(mu_base + MX7ULP_MU_CR) |
|
||||
BIT(0) | BIT(26) | BIT(27), mu_base + MX7ULP_MU_CR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
static const struct dev_pm_ops mu_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(mu_suspend, mu_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver imx_mu_driver = {
|
||||
.driver = {
|
||||
.name = "imx-mu-lp",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mu_pm_ops,
|
||||
.of_match_table = imx_mu_ids,
|
||||
},
|
||||
.probe = imx_mu_probe,
|
||||
};
|
||||
|
||||
static int __init imx_mu_init(void)
|
||||
{
|
||||
return platform_driver_register(&imx_mu_driver);
|
||||
}
|
||||
subsys_initcall(imx_mu_init);
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2004-2015 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 version 2 as
|
||||
* * published by the Free Software Foundation.
|
||||
* */
|
||||
|
||||
#ifndef __ASM_ARCH_MXC_IOMAP_H__
|
||||
#define __ASM_ARCH_MXC_IOMAP_H__
|
||||
|
||||
#define MX6Q_IO_P2V(x) IMX_IO_P2V(x)
|
||||
#define MX6Q_IO_ADDRESS(x) IOMEM(MX6Q_IO_P2V(x))
|
||||
|
||||
#define MX6Q_L2_BASE_ADDR 0x00a02000
|
||||
#define MX6Q_L2_SIZE 0x1000
|
||||
#define MX6Q_IOMUXC_BASE_ADDR 0x020e0000
|
||||
#define MX6Q_IOMUXC_SIZE 0x4000
|
||||
#define MX6Q_SRC_BASE_ADDR 0x020d8000
|
||||
#define MX6Q_SRC_SIZE 0x4000
|
||||
#define MX6Q_CCM_BASE_ADDR 0x020c4000
|
||||
#define MX6Q_CCM_SIZE 0x4000
|
||||
#define MX6Q_ANATOP_BASE_ADDR 0x020c8000
|
||||
#define MX6Q_ANATOP_SIZE 0x1000
|
||||
#define MX6Q_GPC_BASE_ADDR 0x020dc000
|
||||
#define MX6Q_GPC_SIZE 0x4000
|
||||
#define MX6Q_SEMA4_BASE_ADDR 0x02290000
|
||||
#define MX6Q_SEMA4_SIZE 0x4000
|
||||
#define MX6Q_MMDC_P0_BASE_ADDR 0x021b0000
|
||||
#define MX6Q_MMDC_P0_SIZE 0x4000
|
||||
#define MX6Q_MMDC_P1_BASE_ADDR 0x021b4000
|
||||
#define MX6Q_MMDC_P1_SIZE 0x4000
|
||||
#define MX6Q_AIPS1_BASE_ADDR 0x02000000
|
||||
#define MX6Q_AIPS1_SIZE 0x100000
|
||||
#define MX6Q_AIPS2_BASE_ADDR 0x02100000
|
||||
#define MX6Q_AIPS2_SIZE 0x100000
|
||||
#define MX6Q_AIPS3_BASE_ADDR 0x02200000
|
||||
#define MX6Q_AIPS3_SIZE 0x100000
|
||||
|
||||
#define MX6SX_IRAM_TLB_BASE_ADDR 0x008f8000
|
||||
#define MX6Q_IRAM_TLB_BASE_ADDR 0x00900000
|
||||
#define MX6Q_IRAM_TLB_SIZE 0x4000
|
||||
#define TT_ATTRIB_NON_CACHEABLE_1M 0x802
|
||||
#define MX6_SUSPEND_IRAM_DATA_SIZE 256
|
||||
#define MX6SL_WFI_IRAM_DATA_SIZE 100
|
||||
|
||||
#define MX6_SUSPEND_IRAM_ADDR_OFFSET 0
|
||||
#define MX6_CPUIDLE_IRAM_ADDR_OFFSET 0x1000
|
||||
#endif
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 version 2 as
|
||||
* * published by the Free Software Foundation.
|
||||
* */
|
||||
|
||||
#ifndef __ASM_ARCH_MX7_IOMAP_H__
|
||||
#define __ASM_ARCH_MX7_IOMAP_H__
|
||||
|
||||
#define MX7D_IO_P2V(x) IMX_IO_P2V(x)
|
||||
#define MX7D_IO_ADDRESS(x) IOMEM(MX7D_IO_P2V(x))
|
||||
|
||||
#define MX7D_LPSR_BASE_ADDR 0x30270000
|
||||
#define MX7D_LPSR_SIZE 0x10000
|
||||
#define MX7D_CCM_BASE_ADDR 0x30380000
|
||||
#define MX7D_CCM_SIZE 0x10000
|
||||
#define MX7D_IOMUXC_BASE_ADDR 0x30330000
|
||||
#define MX7D_IOMUXC_SIZE 0x10000
|
||||
#define MX7D_IOMUXC_GPR_BASE_ADDR 0x30340000
|
||||
#define MX7D_IOMUXC_GPR_SIZE 0x10000
|
||||
#define MX7D_ANATOP_BASE_ADDR 0x30360000
|
||||
#define MX7D_ANATOP_SIZE 0x10000
|
||||
#define MX7D_SNVS_BASE_ADDR 0x30370000
|
||||
#define MX7D_SNVS_SIZE 0x10000
|
||||
#define MX7D_GPC_BASE_ADDR 0x303a0000
|
||||
#define MX7D_GPC_SIZE 0x10000
|
||||
#define MX7D_SRC_BASE_ADDR 0x30390000
|
||||
#define MX7D_SRC_SIZE 0x10000
|
||||
#define MX7D_DDRC_BASE_ADDR 0x307a0000
|
||||
#define MX7D_DDRC_SIZE 0x10000
|
||||
#define MX7D_DDRC_PHY_BASE_ADDR 0x30790000
|
||||
#define MX7D_DDRC_PHY_SIZE 0x10000
|
||||
#define MX7D_AIPS1_BASE_ADDR 0x30000000
|
||||
#define MX7D_AIPS1_SIZE 0x400000
|
||||
#define MX7D_AIPS2_BASE_ADDR 0x30400000
|
||||
#define MX7D_AIPS2_SIZE 0x400000
|
||||
#define MX7D_AIPS3_BASE_ADDR 0x30900000
|
||||
#define MX7D_AIPS3_SIZE 0x300000
|
||||
#define MX7D_GIC_BASE_ADDR 0x31000000
|
||||
#define MX7D_GIC_SIZE 0x100000
|
||||
|
||||
#define TT_ATTRIB_NON_CACHEABLE_1M 0x802
|
||||
#define MX7_IRAM_TLB_SIZE 0x4000
|
||||
#define MX7_SUSPEND_OCRAM_SIZE 0x1000
|
||||
#define MX7_CPUIDLE_OCRAM_ADDR_OFFSET 0x1000
|
||||
#define MX7_CPUIDLE_OCRAM_SIZE 0x1000
|
||||
#define MX7_BUSFREQ_OCRAM_ADDR_OFFSET 0x2000
|
||||
#define MX7_BUSFREQ_OCRAM_SIZE 0x1000
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright NXP 2017.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 __ASM_ARCH_MX7ULP_IOMAP_H__
|
||||
#define __ASM_ARCH_MX7ULP_IOMAP_H__
|
||||
|
||||
#define MX7ULP_IO_P2V(x) IMX_IO_P2V(x)
|
||||
#define MX7ULP_IO_ADDRESS(x) IOMEM(MX7ULP_IO_P2V(x))
|
||||
|
||||
#define MX7ULP_AIPS1_BASE_ADDR 0x40000000
|
||||
#define MX7ULP_AIPS1_SIZE 0x100000
|
||||
#define MX7ULP_AIPS2_BASE_ADDR 0x40300000
|
||||
#define MX7ULP_AIPS2_SIZE 0x100000
|
||||
#define MX7ULP_AIPS3_BASE_ADDR 0x40400000
|
||||
#define MX7ULP_AIPS3_SIZE 0x100000
|
||||
#define MX7ULP_AIPS4_BASE_ADDR 0x40a00000
|
||||
#define MX7ULP_AIPS4_SIZE 0x100000
|
||||
#define MX7ULP_AIPS5_BASE_ADDR 0x41000000
|
||||
#define MX7ULP_AIPS5_SIZE 0x100000
|
||||
#define MX7ULP_GPIOC_BASE_ADDR 0x400f0000
|
||||
#define MX7ULP_GPIOC_SIZE 0x1000
|
||||
#define MX7ULP_PCC3_BASE_ADDR 0x40b30000
|
||||
#define MX7ULP_PCC3_SIZE 0x1000
|
||||
#define MX7ULP_SCG1_BASE_ADDR 0x403e0000
|
||||
#define MX7ULP_SCG1_SIZE 0x1000
|
||||
#define MX7ULP_PCC2_BASE_ADDR 0x403f0000
|
||||
#define MX7ULP_PCC2_SIZE 0x1000
|
||||
#define MX7ULP_SIM_BASE_ADDR 0x410a3000
|
||||
#define MX7ULP_SIM_SIZE 0x1000
|
||||
#define MX7ULP_PMC1_BASE_ADDR 0x40400000
|
||||
#define MX7ULP_PMC1_SIZE 0x1000
|
||||
#define MX7ULP_SMC1_BASE_ADDR 0x40410000
|
||||
#define MX7ULP_SMC1_SIZE 0x1000
|
||||
#define MX7ULP_MMDC_BASE_ADDR 0x40ab0000
|
||||
#define MX7ULP_MMDC_SIZE 0x1000
|
||||
#define MX7ULP_IOMUXC1_BASE_ADDR 0x40ac0000
|
||||
#define MX7ULP_IOMUXC1_BASE__SIZE 0x1000
|
||||
#define MX7ULP_MMDC_IO_BASE_ADDR 0x40ad0000
|
||||
#define MX7ULP_MMDC_IO_SIZE 0x1000
|
||||
|
||||
/* below is just used for static mapping of the AIPSx's memory region */
|
||||
#define MX7ULP_AIPS_VIRT_BASE(x) (0xf4000000 + ((x) * SZ_1M))
|
||||
|
||||
#define mx7ulp_aips_map_entry(index, _type) { \
|
||||
.virtual = MX7ULP_AIPS_VIRT_BASE(index), \
|
||||
.pfn = __phys_to_pfn(MX7ULP_AIPS ## index ## _BASE_ADDR), \
|
||||
.length = SZ_1M, \
|
||||
.type = _type, \
|
||||
}
|
||||
|
||||
#define TT_ATTRIB_NON_CACHEABLE_1M 0x802
|
||||
#define MX7ULP_IRAM_TLB_SIZE 0x4000
|
||||
#define MX7ULP_SUSPEND_OCRAM_SIZE 0x1000
|
||||
|
||||
#endif
|
|
@ -33,7 +33,13 @@
|
|||
#define MXC_CPU_IMX7D 0x72
|
||||
#define MXC_CPU_IMX7ULP 0xff
|
||||
|
||||
#define IMX_DDR_TYPE_DDR3 0
|
||||
#define IMX_DDR_TYPE_LPDDR2 1
|
||||
#define IMX_DDR_TYPE_LPDDR3 2
|
||||
#define IMX_MMDC_DDR_TYPE_LPDDR3 3
|
||||
|
||||
#define IMX_LPDDR2_1CH_MODE 0
|
||||
#define IMX_LPDDR2_2CH_MODE 1
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
extern unsigned int __mxc_cpu_type;
|
||||
|
@ -85,11 +91,28 @@ static inline bool cpu_is_imx6q(void)
|
|||
return __mxc_cpu_type == MXC_CPU_IMX6Q;
|
||||
}
|
||||
|
||||
static inline bool cpu_is_imx6(void)
|
||||
{
|
||||
return __mxc_cpu_type == MXC_CPU_IMX6Q ||
|
||||
__mxc_cpu_type == MXC_CPU_IMX6DL ||
|
||||
__mxc_cpu_type == MXC_CPU_IMX6SL ||
|
||||
__mxc_cpu_type == MXC_CPU_IMX6SX ||
|
||||
__mxc_cpu_type == MXC_CPU_IMX6UL ||
|
||||
__mxc_cpu_type == MXC_CPU_IMX6ULL ||
|
||||
__mxc_cpu_type == MXC_CPU_IMX6SLL ||
|
||||
__mxc_cpu_type == MXC_CPU_IMX6ULZ;
|
||||
}
|
||||
|
||||
static inline bool cpu_is_imx7d(void)
|
||||
{
|
||||
return __mxc_cpu_type == MXC_CPU_IMX7D;
|
||||
}
|
||||
|
||||
static inline bool cpu_is_imx7ulp(void)
|
||||
{
|
||||
return __mxc_cpu_type == MXC_CPU_IMX7ULP;
|
||||
}
|
||||
|
||||
struct cpu_op {
|
||||
u32 cpu_rate;
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "hardware.h"
|
||||
|
||||
u32 g_diag_reg;
|
||||
static void __iomem *scu_base;
|
||||
void __iomem *scu_base;
|
||||
|
||||
static struct map_desc scu_io_desc __initdata = {
|
||||
/* .virtual and .pfn are run-time assigned */
|
||||
|
@ -47,15 +47,39 @@ static int imx_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define MXC_ARCH_CA7 0xc07
|
||||
static unsigned long __mxc_arch_type;
|
||||
|
||||
static inline bool arm_is_ca7(void)
|
||||
{
|
||||
return __mxc_arch_type == MXC_ARCH_CA7;
|
||||
}
|
||||
/*
|
||||
* Initialise the CPU possible map early - this describes the CPUs
|
||||
* which may be present or become present in the system.
|
||||
*/
|
||||
static void __init imx_smp_init_cpus(void)
|
||||
{
|
||||
unsigned long arch_type;
|
||||
int i, ncores;
|
||||
|
||||
ncores = scu_get_core_count(scu_base);
|
||||
asm volatile(
|
||||
".align 4\n"
|
||||
"mrc p15, 0, %0, c0, c0, 0\n"
|
||||
: "=r" (arch_type)
|
||||
);
|
||||
/* MIDR[15:4] defines ARCH type */
|
||||
__mxc_arch_type = (arch_type >> 4) & 0xfff;
|
||||
|
||||
if (arm_is_ca7()) {
|
||||
unsigned long val;
|
||||
|
||||
/* CA7 core number, [25:24] of CP15 L2CTLR */
|
||||
asm volatile("mrc p15, 1, %0, c9, c0, 2" : "=r" (val));
|
||||
ncores = ((val >> 24) & 0x3) + 1;
|
||||
} else {
|
||||
ncores = scu_get_core_count(scu_base);
|
||||
}
|
||||
|
||||
for (i = ncores; i < NR_CPUS; i++)
|
||||
set_cpu_possible(i, false);
|
||||
|
@ -63,11 +87,15 @@ static void __init imx_smp_init_cpus(void)
|
|||
|
||||
void imx_smp_prepare(void)
|
||||
{
|
||||
if (arm_is_ca7())
|
||||
return;
|
||||
scu_enable(scu_base);
|
||||
}
|
||||
|
||||
static void __init imx_smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
if (arm_is_ca7())
|
||||
return;
|
||||
imx_smp_prepare();
|
||||
|
||||
/*
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -5,64 +5,814 @@
|
|||
* Author: Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/psci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/tlb.h>
|
||||
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define MU_SR 0x60
|
||||
|
||||
#define PMPROT 0x8
|
||||
#define PMCTRL 0x10
|
||||
#define PMSTAT 0x18
|
||||
#define SRS 0x20
|
||||
#define RPC 0x24
|
||||
#define SSRS 0x28
|
||||
#define SRIE 0x2c
|
||||
#define SRIF 0x30
|
||||
#define CSRE 0x34
|
||||
#define MR 0x40
|
||||
|
||||
#define PMC1_HSRUN 0x4
|
||||
#define PMC1_RUN 0x8
|
||||
#define PMC1_VLPR 0xc
|
||||
#define PMC1_STOP 0x10
|
||||
#define PMC1_VLPS 0x14
|
||||
#define PMC1_LLS 0x18
|
||||
#define PMC1_VLLS 0x1c
|
||||
#define PMC1_STATUS 0x20
|
||||
#define PMC1_CTRL 0x24
|
||||
#define PMC0_CTRL 0x28
|
||||
|
||||
#define BM_PMPROT_AHSRUN (1 << 7)
|
||||
#define BM_PMPROT_AVLP (1 << 5)
|
||||
#define BM_PMPROT_ALLS (1 << 3)
|
||||
#define BM_PMPROT_AVLLS (1 << 1)
|
||||
|
||||
#define BM_PMCTRL_STOPA (1 << 24)
|
||||
#define BM_PMCTRL_PSTOPO (3 << 16)
|
||||
#define BM_PMCTRL_RUNM (3 << 8)
|
||||
#define BM_PMCTRL_STOPM (7 << 0)
|
||||
|
||||
#define BM_VLPS_RBBEN (1 << 28)
|
||||
|
||||
#define BM_CTRL_LDOEN (1 << 31)
|
||||
#define BM_CTRL_LDOOKDIS (1 << 30)
|
||||
|
||||
#define BM_VLLS_MON1P2HVDHP (1 << 5)
|
||||
#define BM_VLLS_MON1P2LVDHP (1 << 4)
|
||||
|
||||
#define SMC_PMCTRL 0x10
|
||||
#define BP_PMCTRL_PSTOPO 16
|
||||
#define PSTOPO_PSTOP3 0x3
|
||||
#define PSTOPO_PSTOP2 0x2
|
||||
#define PSTOPO_PSTOP1 0x1
|
||||
#define BP_PMCTRL_RUNM 8
|
||||
#define RUNM_RUN 0
|
||||
#define BP_PMCTRL_STOPM 0
|
||||
#define STOPM_STOP 0
|
||||
#define BP_PMCTRL_PSTOPO 16
|
||||
|
||||
#define BM_PMCTRL_PSTOPO (3 << BP_PMCTRL_PSTOPO)
|
||||
#define BM_PMCTRL_RUNM (3 << BP_PMCTRL_RUNM)
|
||||
#define BM_PMCTRL_STOPM (7 << BP_PMCTRL_STOPM)
|
||||
#define MX7ULP_MAX_MMDC_IO_NUM 64
|
||||
#define MX7ULP_MAX_MMDC_NUM 50
|
||||
#define MX7ULP_MAX_IOMUX_NUM 116
|
||||
#define MX7ULP_MAX_SELECT_INPUT_NUM 78
|
||||
|
||||
#define IOMUX_START 0x0
|
||||
#define SELECT_INPUT_START 0x200
|
||||
|
||||
#define TPM_SC 0x10
|
||||
#define TPM_MOD 0x18
|
||||
#define TPM_C0SC 0x20
|
||||
#define TPM_C0V 0x24
|
||||
|
||||
#define PCC2_ENABLE_PCS_FIRC ((1 << 30) | (3 << 24))
|
||||
#define PCC2_ENABLE (1 << 30)
|
||||
|
||||
#define LPUART_BAUD 0x10
|
||||
#define LPUART_CTRL 0x18
|
||||
#define LPUART_FIFO 0x28
|
||||
#define LPUART_WATER 0x2c
|
||||
|
||||
#define GPIO_PDOR 0x0
|
||||
#define GPIO_PDDR 0x14
|
||||
|
||||
#define PTC2_LPUART4_TX_OFFSET 0x8
|
||||
#define PTC3_LPUART4_RX_OFFSET 0xc
|
||||
#define PTC2_LPUART4_TX_INPUT_OFFSET 0x248
|
||||
#define PTC3_LPUART4_RX_INPUT_OFFSET 0x24c
|
||||
#define LPUART4_MUX_VALUE (4 << 8)
|
||||
#define LPUART4_INPUT_VALUE (1)
|
||||
|
||||
#define MU_B_SR_NMIC (1 << 3)
|
||||
|
||||
#define DGO_GPR3 0x60
|
||||
#define DGO_GPR4 0x64
|
||||
|
||||
#define ADDR_1M_MASK 0xFFF00000
|
||||
|
||||
static void __iomem *smc1_base;
|
||||
static void __iomem *pmc0_base;
|
||||
static void __iomem *pmc1_base;
|
||||
static void __iomem *tpm5_base;
|
||||
static void __iomem *lpuart4_base;
|
||||
static void __iomem *iomuxc1_base;
|
||||
static void __iomem *pcc2_base;
|
||||
static void __iomem *pcc3_base;
|
||||
static void __iomem *mu_base;
|
||||
static void __iomem *scg1_base;
|
||||
static void __iomem *gpio_base[4];
|
||||
static void __iomem *suspend_ocram_base;
|
||||
static void (*imx7ulp_suspend_in_ocram_fn)(void __iomem *sram_base);
|
||||
|
||||
static u32 tpm5_regs[4];
|
||||
static u32 lpuart4_regs[4];
|
||||
static u32 pcc2_regs[24][2] = {
|
||||
{0x20, 0}, {0x3c, 0}, {0x40, 0}, {0x6c, 0},
|
||||
{0x84, 0}, {0x90, 0}, {0x94, 0}, {0x98, 0},
|
||||
{0x9c, 0}, {0xa4, 0}, {0xa8, 0}, {0xac, 0},
|
||||
{0xb0, 0}, {0xb4, 0}, {0xb8, 0}, {0xc4, 0},
|
||||
{0xcc, 0}, {0xd0, 0}, {0xd4, 0}, {0xd8, 0},
|
||||
{0xdc, 0}, {0xe0, 0}, {0xf4, 0}, {0x10c, 0},
|
||||
};
|
||||
|
||||
static u32 pcc3_regs[16][2] = {
|
||||
{0x84, 0}, {0x88, 0}, {0x90, 0}, {0x94, 0},
|
||||
{0x98, 0}, {0x9c, 0}, {0xa0, 0}, {0xa4, 0},
|
||||
{0xa8, 0}, {0xac, 0}, {0xb8, 0}, {0xbc, 0},
|
||||
{0xc0, 0}, {0xc4, 0}, {0x140, 0}, {0x144, 0},
|
||||
};
|
||||
|
||||
static u32 scg1_offset[17] = {
|
||||
0x14, 0x30, 0x40, 0x304,
|
||||
0x500, 0x504, 0x508, 0x50c,
|
||||
0x510, 0x514, 0x600, 0x604,
|
||||
0x608, 0x60c, 0x610, 0x614,
|
||||
0x104,
|
||||
};
|
||||
|
||||
extern unsigned long iram_tlb_base_addr;
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
|
||||
/*
|
||||
* suspend ocram space layout:
|
||||
* ======================== high address ======================
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* ^
|
||||
* ^
|
||||
* ^
|
||||
* imx7ulp_suspend code
|
||||
* PM_INFO structure(imx7ulp_cpu_pm_info)
|
||||
* ======================== low address =======================
|
||||
*/
|
||||
struct imx7ulp_pm_socdata {
|
||||
u32 ddr_type;
|
||||
const char *mmdc_compat;
|
||||
const u32 mmdc_io_num;
|
||||
const u32 *mmdc_io_offset;
|
||||
const u32 mmdc_num;
|
||||
const u32 *mmdc_offset;
|
||||
};
|
||||
|
||||
static const u32 imx7ulp_mmdc_io_lpddr3_offset[] __initconst = {
|
||||
0x0, 0x4, 0x8, 0xc,
|
||||
0x10, 0x14, 0x18, 0x1c,
|
||||
0x20, 0x24, 0x28, 0x2c,
|
||||
0x30, 0x34, 0x38, 0x3c,
|
||||
0x40, 0x44, 0x48, 0x4c,
|
||||
0x50, 0x54, 0x58, 0x5c,
|
||||
0x60, 0x64, 0x68, 0x6c,
|
||||
0x70, 0x74, 0x78, 0x7c,
|
||||
0x80, 0x84, 0x88, 0x8c,
|
||||
0x90, 0x94, 0x98, 0x9c,
|
||||
0xa0, 0xa4, 0xa8, 0xac,
|
||||
0xb0, 0xb4, 0xb8, 0xbc,
|
||||
0xc0, 0xc4, 0xc8, 0xcc,
|
||||
0xd0, 0xd4, 0xd8, 0xdc,
|
||||
0xe8, 0xf8, 0xfc, 0x120,
|
||||
0x124,
|
||||
};
|
||||
|
||||
static const u32 imx7ulp_mmdc_lpddr3_offset[] __initconst = {
|
||||
0x01c, 0x800, 0x85c, 0x890,
|
||||
0x848, 0x850, 0x81c, 0x820,
|
||||
0x824, 0x828, 0x82c, 0x830,
|
||||
0x834, 0x838, 0x8c0, 0x8b8,
|
||||
0x004, 0x00c, 0x010, 0x038,
|
||||
0x014, 0x018, 0x02c, 0x030,
|
||||
0x040, 0x000, 0x01c, 0x01c,
|
||||
0x01c, 0x01c, 0x01c, 0x01c,
|
||||
0x01c, 0x01c, 0x01c, 0x01c,
|
||||
0x01c, 0x01c, 0x83c, 0x020,
|
||||
0x800, 0x004, 0x404, 0x01c,
|
||||
};
|
||||
|
||||
static const u32 imx7ulp_lpddr3_script[] __initconst = {
|
||||
0x00008000, 0xA1390003, 0x0D3900A0, 0x00400000,
|
||||
0x40404040, 0x40404040, 0x33333333, 0x33333333,
|
||||
0x33333333, 0x33333333, 0xf3333333, 0xf3333333,
|
||||
0xf3333333, 0xf3333333, 0x24922492, 0x00000800,
|
||||
0x00020052, 0x292C42F3, 0x00100A22, 0x00120556,
|
||||
0x00C700DB, 0x00211718, 0x0F9F26D2, 0x009F0E10,
|
||||
0x0000003F, 0xC3190000, 0x00008050, 0x00008058,
|
||||
0x003F8030, 0x003F8038, 0xFF0A8030, 0xFF0A8038,
|
||||
0x04028030, 0x04028038, 0x83018030, 0x83018038,
|
||||
0x01038030, 0x01038038, 0x20000000, 0x00001800,
|
||||
0xA1310000, 0x00020052, 0x00011006, 0x00000000,
|
||||
};
|
||||
|
||||
static const struct imx7ulp_pm_socdata imx7ulp_lpddr3_pm_data __initconst = {
|
||||
.mmdc_compat = "fsl,imx7ulp-mmdc",
|
||||
.mmdc_io_num = ARRAY_SIZE(imx7ulp_mmdc_io_lpddr3_offset),
|
||||
.mmdc_io_offset = imx7ulp_mmdc_io_lpddr3_offset,
|
||||
.mmdc_num = ARRAY_SIZE(imx7ulp_mmdc_lpddr3_offset),
|
||||
.mmdc_offset = imx7ulp_mmdc_lpddr3_offset,
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is for passing necessary data for low level ocram
|
||||
* suspend code(arch/arm/mach-imx/suspend-imx7ulp.S), if this struct
|
||||
* definition is changed, the offset definition in
|
||||
* arch/arm/mach-imx/suspend-imx7ulp.S must be also changed accordingly,
|
||||
* otherwise, the suspend to sram function will be broken!
|
||||
*/
|
||||
struct imx7ulp_cpu_pm_info {
|
||||
u32 m4_reserve0;
|
||||
u32 m4_reserve1;
|
||||
u32 m4_reserve2;
|
||||
phys_addr_t pbase; /* The physical address of pm_info. */
|
||||
phys_addr_t resume_addr; /* The physical resume address for asm code */
|
||||
u32 pm_info_size; /* Size of pm_info. */
|
||||
void __iomem *sim_base;
|
||||
void __iomem *scg1_base;
|
||||
void __iomem *mmdc_base;
|
||||
void __iomem *mmdc_io_base;
|
||||
void __iomem *smc1_base;
|
||||
u32 scg1[17];
|
||||
u32 ttbr1; /* Store TTBR1 */
|
||||
u32 gpio[4][2];
|
||||
u32 iomux_num; /* Number of IOs which need saved/restored. */
|
||||
u32 iomux_val[MX7ULP_MAX_IOMUX_NUM]; /* To save value */
|
||||
u32 select_input_num; /* Number of select input which need saved/restored. */
|
||||
u32 select_input_val[MX7ULP_MAX_SELECT_INPUT_NUM]; /* To save value */
|
||||
u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
|
||||
u32 mmdc_io_val[MX7ULP_MAX_MMDC_IO_NUM][2]; /* To save offset and value */
|
||||
u32 mmdc_num; /* Number of MMDC registers which need saved/restored. */
|
||||
u32 mmdc_val[MX7ULP_MAX_MMDC_NUM][2];
|
||||
} __aligned(8);
|
||||
|
||||
static struct imx7ulp_cpu_pm_info *pm_info;
|
||||
static void __iomem *aips1_base;
|
||||
static void __iomem *aips2_base;
|
||||
static void __iomem *aips3_base;
|
||||
static void __iomem *aips4_base;
|
||||
static void __iomem *aips5_base;
|
||||
|
||||
static const char * const low_power_ocram_match[] __initconst = {
|
||||
"fsl,lpm-sram",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void imx7ulp_gpio_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
pm_info->gpio[i][0] = readl_relaxed(gpio_base[i] + GPIO_PDOR);
|
||||
pm_info->gpio[i][1] = readl_relaxed(gpio_base[i] + GPIO_PDDR);
|
||||
}
|
||||
}
|
||||
|
||||
static void imx7ulp_scg1_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 17; i++)
|
||||
pm_info->scg1[i] = readl_relaxed(scg1_base + scg1_offset[i]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc3_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
pcc3_regs[i][1] = readl_relaxed(pcc3_base + pcc3_regs[i][0]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc3_restore(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
writel_relaxed(pcc3_regs[i][1], pcc3_base + pcc3_regs[i][0]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc2_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
pcc2_regs[i][1] = readl_relaxed(pcc2_base + pcc2_regs[i][0]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc2_restore(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
writel_relaxed(pcc2_regs[i][1], pcc2_base + pcc2_regs[i][0]);
|
||||
}
|
||||
|
||||
static inline void imx7ulp_iomuxc_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
pm_info->iomux_num = MX7ULP_MAX_IOMUX_NUM;
|
||||
pm_info->select_input_num = MX7ULP_MAX_SELECT_INPUT_NUM;
|
||||
|
||||
for (i = 0; i < pm_info->iomux_num; i++)
|
||||
pm_info->iomux_val[i] =
|
||||
readl_relaxed(iomuxc1_base +
|
||||
IOMUX_START + i * 0x4);
|
||||
for (i = 0; i < pm_info->select_input_num; i++)
|
||||
pm_info->select_input_val[i] =
|
||||
readl_relaxed(iomuxc1_base +
|
||||
SELECT_INPUT_START + i * 0x4);
|
||||
}
|
||||
|
||||
static void imx7ulp_lpuart_save(void)
|
||||
{
|
||||
lpuart4_regs[0] = readl_relaxed(lpuart4_base + LPUART_BAUD);
|
||||
lpuart4_regs[1] = readl_relaxed(lpuart4_base + LPUART_FIFO);
|
||||
lpuart4_regs[2] = readl_relaxed(lpuart4_base + LPUART_WATER);
|
||||
lpuart4_regs[3] = readl_relaxed(lpuart4_base + LPUART_CTRL);
|
||||
}
|
||||
|
||||
static void imx7ulp_lpuart_restore(void)
|
||||
{
|
||||
writel_relaxed(LPUART4_MUX_VALUE,
|
||||
iomuxc1_base + PTC2_LPUART4_TX_OFFSET);
|
||||
writel_relaxed(LPUART4_MUX_VALUE,
|
||||
iomuxc1_base + PTC3_LPUART4_RX_OFFSET);
|
||||
writel_relaxed(LPUART4_INPUT_VALUE,
|
||||
iomuxc1_base + PTC2_LPUART4_TX_INPUT_OFFSET);
|
||||
writel_relaxed(LPUART4_INPUT_VALUE,
|
||||
iomuxc1_base + PTC3_LPUART4_RX_INPUT_OFFSET);
|
||||
|
||||
writel_relaxed(lpuart4_regs[0], lpuart4_base + LPUART_BAUD);
|
||||
writel_relaxed(lpuart4_regs[1], lpuart4_base + LPUART_FIFO);
|
||||
writel_relaxed(lpuart4_regs[2], lpuart4_base + LPUART_WATER);
|
||||
writel_relaxed(lpuart4_regs[3], lpuart4_base + LPUART_CTRL);
|
||||
}
|
||||
|
||||
static void imx7ulp_tpm_save(void)
|
||||
{
|
||||
tpm5_regs[0] = readl_relaxed(tpm5_base + TPM_SC);
|
||||
tpm5_regs[1] = readl_relaxed(tpm5_base + TPM_MOD);
|
||||
tpm5_regs[2] = readl_relaxed(tpm5_base + TPM_C0SC);
|
||||
tpm5_regs[3] = readl_relaxed(tpm5_base + TPM_C0V);
|
||||
}
|
||||
|
||||
static void imx7ulp_tpm_restore(void)
|
||||
{
|
||||
writel_relaxed(tpm5_regs[0], tpm5_base + TPM_SC);
|
||||
writel_relaxed(tpm5_regs[1], tpm5_base + TPM_MOD);
|
||||
writel_relaxed(tpm5_regs[2], tpm5_base + TPM_C0SC);
|
||||
writel_relaxed(tpm5_regs[3], tpm5_base + TPM_C0V);
|
||||
}
|
||||
|
||||
static void imx7ulp_set_dgo(u32 val)
|
||||
{
|
||||
writel_relaxed(val, pm_info->sim_base + DGO_GPR3);
|
||||
writel_relaxed(val, pm_info->sim_base + DGO_GPR4);
|
||||
}
|
||||
|
||||
int imx7ulp_set_lpm(enum ulp_cpu_pwr_mode mode)
|
||||
{
|
||||
u32 val = readl_relaxed(smc1_base + SMC_PMCTRL);
|
||||
u32 val1 = BM_PMPROT_AHSRUN | BM_PMPROT_AVLP | BM_PMPROT_AVLLS;
|
||||
u32 val2 = readl_relaxed(smc1_base + PMCTRL);
|
||||
u32 val3 = readl_relaxed(pmc0_base + PMC0_CTRL);
|
||||
|
||||
/* clear all */
|
||||
val &= ~(BM_PMCTRL_RUNM | BM_PMCTRL_STOPM | BM_PMCTRL_PSTOPO);
|
||||
val2 &= ~(BM_PMCTRL_RUNM |
|
||||
BM_PMCTRL_STOPM | BM_PMCTRL_PSTOPO);
|
||||
val3 |= BM_CTRL_LDOOKDIS;
|
||||
|
||||
switch (mode) {
|
||||
case ULP_PM_RUN:
|
||||
/* system/bus clock enabled */
|
||||
val |= PSTOPO_PSTOP3 << BP_PMCTRL_PSTOPO;
|
||||
val2 |= 0x3 << BP_PMCTRL_PSTOPO;
|
||||
break;
|
||||
case ULP_PM_WAIT:
|
||||
/* system clock disabled, bus clock enabled */
|
||||
val |= PSTOPO_PSTOP2 << BP_PMCTRL_PSTOPO;
|
||||
val2 |= 0x2 << BP_PMCTRL_PSTOPO;
|
||||
break;
|
||||
case ULP_PM_STOP:
|
||||
/* system/bus clock disabled */
|
||||
val |= PSTOPO_PSTOP1 << BP_PMCTRL_PSTOPO;
|
||||
val2 |= 0x1 << BP_PMCTRL_PSTOPO;
|
||||
break;
|
||||
case ULP_PM_VLPS:
|
||||
val2 |= 0x2 << BP_PMCTRL_STOPM;
|
||||
break;
|
||||
case ULP_PM_VLLS:
|
||||
val2 |= 0x4 << BP_PMCTRL_STOPM;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel_relaxed(val, smc1_base + SMC_PMCTRL);
|
||||
writel_relaxed(val1, smc1_base + PMPROT);
|
||||
writel_relaxed(val2, smc1_base + PMCTRL);
|
||||
writel_relaxed(val3, pmc0_base + PMC0_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_init(void)
|
||||
#define MX7ULP_SUSPEND_POWERDWN_PARAM \
|
||||
((0 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
#define MX7ULP_SUSPEND_STANDBY_PARAM \
|
||||
((0 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_STANDBY << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
static int imx7ulp_suspend_finish(unsigned long val)
|
||||
{
|
||||
u32 state;
|
||||
|
||||
if (val == 0)
|
||||
state = MX7ULP_SUSPEND_POWERDWN_PARAM;
|
||||
else
|
||||
state = MX7ULP_SUSPEND_STANDBY_PARAM;
|
||||
|
||||
if (psci_ops.cpu_suspend)
|
||||
return psci_ops.cpu_suspend(state, __pa(cpu_resume));
|
||||
|
||||
imx7ulp_suspend_in_ocram_fn(suspend_ocram_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_pm_enter(suspend_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case PM_SUSPEND_STANDBY:
|
||||
if (psci_ops.cpu_suspend)
|
||||
/* Zzz ... */
|
||||
cpu_suspend(1, imx7ulp_suspend_finish);
|
||||
else {
|
||||
imx7ulp_set_lpm(ULP_PM_VLPS);
|
||||
writel_relaxed(
|
||||
readl_relaxed(pmc1_base + PMC1_VLPS) | BM_VLPS_RBBEN,
|
||||
pmc1_base + PMC1_VLPS);
|
||||
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
|
||||
writel_relaxed(
|
||||
readl_relaxed(pmc1_base + PMC1_VLPS) & ~BM_VLPS_RBBEN,
|
||||
pmc1_base + PMC1_VLPS);
|
||||
imx7ulp_set_lpm(ULP_PM_RUN);
|
||||
}
|
||||
break;
|
||||
case PM_SUSPEND_MEM:
|
||||
if (psci_ops.cpu_suspend) {
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
} else {
|
||||
imx7ulp_gpio_save();
|
||||
imx7ulp_scg1_save();
|
||||
imx7ulp_pcc2_save();
|
||||
imx7ulp_pcc3_save();
|
||||
imx7ulp_tpm_save();
|
||||
if (!console_suspend_enabled)
|
||||
imx7ulp_lpuart_save();
|
||||
imx7ulp_iomuxc_save();
|
||||
imx7ulp_set_lpm(ULP_PM_VLLS);
|
||||
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
|
||||
imx7ulp_pcc2_restore();
|
||||
imx7ulp_pcc3_restore();
|
||||
if (!console_suspend_enabled)
|
||||
imx7ulp_lpuart_restore();
|
||||
imx7ulp_set_dgo(0);
|
||||
imx7ulp_tpm_restore();
|
||||
imx7ulp_set_lpm(ULP_PM_RUN);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Put CA7 into VLLS mode before M4 power off CA7 */
|
||||
void imx7ulp_poweroff(void)
|
||||
{
|
||||
imx7ulp_set_lpm(ULP_PM_VLLS);
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
}
|
||||
|
||||
static int imx7ulp_pm_valid(suspend_state_t state)
|
||||
{
|
||||
return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM);
|
||||
}
|
||||
|
||||
static const struct platform_suspend_ops imx7ulp_pm_ops = {
|
||||
.enter = imx7ulp_pm_enter,
|
||||
.valid = imx7ulp_pm_valid,
|
||||
};
|
||||
|
||||
static int __init imx7ulp_suspend_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
suspend_set_ops(&imx7ulp_pm_ops);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct map_desc iram_tlb_io_desc __initdata = {
|
||||
/* .virtual and .pfn are run-time assigned */
|
||||
.length = SZ_1M,
|
||||
.type = MT_MEMORY_RWX_NONCACHED,
|
||||
};
|
||||
|
||||
static int __init imx7ulp_dt_find_lpsram(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
unsigned long lpram_addr;
|
||||
const __be32 *prop = of_get_flat_dt_prop(node, "reg", NULL);
|
||||
|
||||
if (of_flat_dt_match(node, low_power_ocram_match)) {
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
|
||||
lpram_addr = be32_to_cpup(prop);
|
||||
|
||||
/* We need to create a 1M page table entry. */
|
||||
iram_tlb_io_desc.virtual =
|
||||
IMX_IO_P2V(lpram_addr & ADDR_1M_MASK);
|
||||
iram_tlb_io_desc.pfn = __phys_to_pfn(lpram_addr & ADDR_1M_MASK);
|
||||
iram_tlb_phys_addr = lpram_addr;
|
||||
iram_tlb_base_addr = IMX_IO_P2V(lpram_addr);
|
||||
iotable_init(&iram_tlb_io_desc, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_map_io(void)
|
||||
{
|
||||
/*
|
||||
* Get the address of IRAM or OCRAM to be used by the low
|
||||
* power code from the device tree.
|
||||
*/
|
||||
WARN_ON(of_scan_flat_dt(imx7ulp_dt_find_lpsram, NULL));
|
||||
|
||||
/* Return if no IRAM space is allocated for suspend/resume code. */
|
||||
if (!iram_tlb_base_addr) {
|
||||
pr_warn("No valid ocram available for suspend/resume!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_common_init(const struct imx7ulp_pm_socdata
|
||||
*socdata)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned long sram_paddr = 0;
|
||||
const u32 *mmdc_offset_array;
|
||||
const u32 *mmdc_io_offset_array;
|
||||
unsigned long i, j;
|
||||
int ret;
|
||||
|
||||
if (psci_ops.cpu_suspend) {
|
||||
aips1_base = ioremap(MX7ULP_AIPS1_BASE_ADDR, SZ_1M);
|
||||
aips2_base = ioremap(MX7ULP_AIPS2_BASE_ADDR, SZ_1M);
|
||||
aips3_base = ioremap(MX7ULP_AIPS3_BASE_ADDR, SZ_1M);
|
||||
aips4_base = ioremap(MX7ULP_AIPS4_BASE_ADDR, SZ_1M);
|
||||
aips5_base = ioremap(MX7ULP_AIPS5_BASE_ADDR, SZ_1M);
|
||||
} else {
|
||||
/* Set all entries to 0 except first 3 words reserved for M4. */
|
||||
memset((void *)iram_tlb_base_addr, 0, MX7ULP_IRAM_TLB_SIZE);
|
||||
|
||||
/*
|
||||
* Make sure the IRAM virtual address has a mapping in the IRAM
|
||||
* page table.
|
||||
*
|
||||
* Only use the top 12 bits [31-20] when storing the physical
|
||||
* address in the page table as only these bits are required
|
||||
* for 1M mapping.
|
||||
*/
|
||||
j = ((iram_tlb_base_addr >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
(iram_tlb_phys_addr & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS1 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips1_base = ioremap(MX7ULP_AIPS1_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips1_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS1_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS2 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips2_base = ioremap(MX7ULP_AIPS2_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips2_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS2_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS3 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips3_base = ioremap(MX7ULP_AIPS3_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips3_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS3_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS4 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips4_base = ioremap(MX7ULP_AIPS4_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips4_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS4_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS5 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips5_base = ioremap(MX7ULP_AIPS5_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips5_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS5_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
}
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-smc1");
|
||||
smc1_base = of_iomap(np, 0);
|
||||
WARN_ON(!smc1_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pmc0");
|
||||
pmc0_base = of_iomap(np, 0);
|
||||
WARN_ON(!pmc0_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pmc1");
|
||||
pmc1_base = of_iomap(np, 0);
|
||||
WARN_ON(!pmc1_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-tpm");
|
||||
tpm5_base = of_iomap(np, 0);
|
||||
WARN_ON(!tpm5_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-lpuart");
|
||||
lpuart4_base = of_iomap(np, 0);
|
||||
WARN_ON(!lpuart4_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pcc2");
|
||||
pcc2_base = of_iomap(np, 0);
|
||||
WARN_ON(!pcc2_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pcc3");
|
||||
pcc3_base = of_iomap(np, 0);
|
||||
WARN_ON(!pcc3_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-iomuxc1");
|
||||
iomuxc1_base = of_iomap(np, 0);
|
||||
WARN_ON(!iomuxc1_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-scg1");
|
||||
scg1_base = of_iomap(np, 0);
|
||||
WARN_ON(!scg1_base);
|
||||
|
||||
np = NULL;
|
||||
for (i = 0; i < 4; i++) {
|
||||
np = of_find_compatible_node(np, NULL, "fsl,vf610-gpio");
|
||||
gpio_base[i] = of_iomap(np, 1);
|
||||
WARN_ON(!gpio_base[i]);
|
||||
}
|
||||
|
||||
if (psci_ops.cpu_suspend) {
|
||||
pm_info = kzalloc(SZ_16K, GFP_KERNEL);
|
||||
if (!pm_info)
|
||||
panic("pm info allocation failed\n");
|
||||
} else {
|
||||
/*
|
||||
* 16KB is allocated for IRAM TLB, but only up 8k is for kernel TLB,
|
||||
* The lower 8K is not used, so use the lower 8K for IRAM code and
|
||||
* pm_info.
|
||||
*
|
||||
*/
|
||||
sram_paddr = iram_tlb_phys_addr;
|
||||
|
||||
/* Make sure sram_paddr is 8 byte aligned. */
|
||||
if ((uintptr_t)(sram_paddr) & (FNCPY_ALIGN - 1))
|
||||
sram_paddr += FNCPY_ALIGN - sram_paddr % (FNCPY_ALIGN);
|
||||
|
||||
/* Get the virtual address of the suspend code. */
|
||||
suspend_ocram_base = (void *)IMX_IO_P2V(sram_paddr);
|
||||
|
||||
pm_info = suspend_ocram_base;
|
||||
}
|
||||
pm_info->pbase = sram_paddr;
|
||||
pm_info->resume_addr = virt_to_phys(imx7ulp_cpu_resume);
|
||||
pm_info->pm_info_size = sizeof(*pm_info);
|
||||
|
||||
pm_info->scg1_base = aips2_base +
|
||||
(MX7ULP_SCG1_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->smc1_base = aips3_base +
|
||||
(MX7ULP_SMC1_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->mmdc_base = aips4_base +
|
||||
(MX7ULP_MMDC_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->mmdc_io_base = aips4_base +
|
||||
(MX7ULP_MMDC_IO_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->sim_base = aips5_base +
|
||||
(MX7ULP_SIM_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
|
||||
pm_info->mmdc_io_num = socdata->mmdc_io_num;
|
||||
mmdc_io_offset_array = socdata->mmdc_io_offset;
|
||||
pm_info->mmdc_num = socdata->mmdc_num;
|
||||
mmdc_offset_array = socdata->mmdc_offset;
|
||||
|
||||
for (i = 0; i < pm_info->mmdc_io_num; i++) {
|
||||
pm_info->mmdc_io_val[i][0] =
|
||||
mmdc_io_offset_array[i];
|
||||
pm_info->mmdc_io_val[i][1] =
|
||||
readl_relaxed(pm_info->mmdc_io_base +
|
||||
mmdc_io_offset_array[i]);
|
||||
}
|
||||
|
||||
/* initialize MMDC settings */
|
||||
for (i = 0; i < pm_info->mmdc_num; i++)
|
||||
pm_info->mmdc_val[i][0] =
|
||||
mmdc_offset_array[i];
|
||||
|
||||
for (i = 0; i < pm_info->mmdc_num; i++)
|
||||
pm_info->mmdc_val[i][1] = imx7ulp_lpddr3_script[i];
|
||||
|
||||
if (!psci_ops.cpu_suspend) {
|
||||
imx7ulp_suspend_in_ocram_fn = fncpy(
|
||||
suspend_ocram_base + sizeof(*pm_info),
|
||||
&imx7ulp_suspend,
|
||||
MX7ULP_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_SUSPEND)) {
|
||||
ret = imx7ulp_suspend_init();
|
||||
if (ret)
|
||||
pr_warn("%s: No DDR LPM support with suspend %d!\n",
|
||||
__func__, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_init(void)
|
||||
{
|
||||
imx7ulp_pm_common_init(&imx7ulp_lpddr3_pm_data);
|
||||
imx7ulp_set_lpm(ULP_PM_RUN);
|
||||
}
|
||||
|
||||
static irqreturn_t imx7ulp_nmi_isr(int irq, void *param)
|
||||
{
|
||||
writel_relaxed(readl_relaxed(mu_base + MU_SR) | MU_B_SR_NMIC,
|
||||
mu_base + MU_SR);
|
||||
pm_system_wakeup();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void imx7ulp_enable_nmi(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int irq, ret;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-nmi");
|
||||
mu_base = of_iomap(np, 0);
|
||||
WARN_ON(!mu_base);
|
||||
irq = of_irq_get(np, 0);
|
||||
ret = request_irq(irq, imx7ulp_nmi_isr,
|
||||
IRQF_NO_SUSPEND, "imx7ulp-nmi", NULL);
|
||||
if (ret) {
|
||||
pr_err("%s: register interrupt %d failed, rc %d\n",
|
||||
__func__, irq, ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* Copyright 2017-2018 NXP
|
||||
*
|
||||
* 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/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/imx_rpmsg.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/rpmsg.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/virtio.h>
|
||||
#include "common.h"
|
||||
|
||||
#define RPMSG_TIMEOUT 1000
|
||||
|
||||
#define PM_RPMSG_TYPE 0
|
||||
#define HEATBEAT_RPMSG_TYPE 2
|
||||
|
||||
enum pm_rpmsg_cmd {
|
||||
PM_RPMSG_MODE,
|
||||
PM_RPMSG_HEART_BEAT,
|
||||
PM_RPMSG_HEART_BEAT_OFF,
|
||||
};
|
||||
|
||||
enum pm_rpmsg_power_mode {
|
||||
PM_RPMSG_HSRUN,
|
||||
PM_RPMSG_RUN,
|
||||
PM_RPMSG_VLPR,
|
||||
PM_RPMSG_WAIT,
|
||||
PM_RPMSG_VLPS,
|
||||
PM_RPMSG_VLLS,
|
||||
PM_RPMSG_REBOOT,
|
||||
PM_RPMSG_SHUTDOWN,
|
||||
};
|
||||
|
||||
struct pm_rpmsg_info {
|
||||
struct rpmsg_device *rpdev;
|
||||
struct device *dev;
|
||||
struct pm_rpmsg_data *msg;
|
||||
struct pm_qos_request pm_qos_req;
|
||||
struct notifier_block restart_handler;
|
||||
struct completion cmd_complete;
|
||||
bool first_flag;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static struct pm_rpmsg_info pm_rpmsg;
|
||||
|
||||
static struct delayed_work heart_beat_work;
|
||||
|
||||
static bool heartbeat_off;
|
||||
|
||||
struct pm_rpmsg_data {
|
||||
struct imx_rpmsg_head header;
|
||||
u8 data;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static int pm_send_message(struct pm_rpmsg_data *msg,
|
||||
struct pm_rpmsg_info *info, bool ack)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!info->rpdev) {
|
||||
dev_dbg(info->dev,
|
||||
"rpmsg channel not ready, m4 image ready?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&info->lock);
|
||||
pm_qos_add_request(&info->pm_qos_req,
|
||||
PM_QOS_CPU_DMA_LATENCY, 0);
|
||||
|
||||
reinit_completion(&info->cmd_complete);
|
||||
|
||||
err = rpmsg_send(info->rpdev->ept, (void *)msg,
|
||||
sizeof(struct pm_rpmsg_data));
|
||||
|
||||
if (err) {
|
||||
dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (ack) {
|
||||
err = wait_for_completion_timeout(&info->cmd_complete,
|
||||
msecs_to_jiffies(RPMSG_TIMEOUT));
|
||||
if (!err) {
|
||||
dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n");
|
||||
err = -ETIMEDOUT;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (info->msg->data != 0) {
|
||||
dev_err(&info->rpdev->dev, "rpmsg not ack %d!\n",
|
||||
info->msg->data);
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
}
|
||||
|
||||
err_out:
|
||||
pm_qos_remove_request(&info->pm_qos_req);
|
||||
mutex_unlock(&info->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pm_vlls_notify_m4(bool enter)
|
||||
{
|
||||
struct pm_rpmsg_data msg;
|
||||
|
||||
msg.header.cate = IMX_RMPSG_LIFECYCLE;
|
||||
msg.header.major = IMX_RMPSG_MAJOR;
|
||||
msg.header.minor = IMX_RMPSG_MINOR;
|
||||
msg.header.type = PM_RPMSG_TYPE;
|
||||
msg.header.cmd = PM_RPMSG_MODE;
|
||||
msg.data = enter ? PM_RPMSG_VLLS : PM_RPMSG_RUN;
|
||||
|
||||
return pm_send_message(&msg, &pm_rpmsg, true);
|
||||
}
|
||||
|
||||
void pm_shutdown_notify_m4(void)
|
||||
{
|
||||
struct pm_rpmsg_data msg;
|
||||
|
||||
msg.header.cate = IMX_RMPSG_LIFECYCLE;
|
||||
msg.header.major = IMX_RMPSG_MAJOR;
|
||||
msg.header.minor = IMX_RMPSG_MINOR;
|
||||
msg.header.type = PM_RPMSG_TYPE;
|
||||
msg.header.cmd = PM_RPMSG_MODE;
|
||||
msg.data = PM_RPMSG_SHUTDOWN;
|
||||
/* No ACK from M4 */
|
||||
pm_send_message(&msg, &pm_rpmsg, false);
|
||||
imx7ulp_poweroff();
|
||||
}
|
||||
|
||||
void pm_reboot_notify_m4(void)
|
||||
{
|
||||
struct pm_rpmsg_data msg;
|
||||
|
||||
msg.header.cate = IMX_RMPSG_LIFECYCLE;
|
||||
msg.header.major = IMX_RMPSG_MAJOR;
|
||||
msg.header.minor = IMX_RMPSG_MINOR;
|
||||
msg.header.type = PM_RPMSG_TYPE;
|
||||
msg.header.cmd = PM_RPMSG_MODE;
|
||||
msg.data = PM_RPMSG_REBOOT;
|
||||
|
||||
pm_send_message(&msg, &pm_rpmsg, true);
|
||||
|
||||
}
|
||||
|
||||
void pm_heartbeat_off_notify_m4(bool enter)
|
||||
{
|
||||
struct pm_rpmsg_data msg;
|
||||
|
||||
msg.header.cate = IMX_RMPSG_LIFECYCLE;
|
||||
msg.header.major = IMX_RMPSG_MAJOR;
|
||||
msg.header.minor = IMX_RMPSG_MINOR;
|
||||
msg.header.type = PM_RPMSG_TYPE;
|
||||
msg.header.cmd = PM_RPMSG_HEART_BEAT_OFF;
|
||||
msg.data = enter ? 0 : 1;
|
||||
|
||||
pm_send_message(&msg, &pm_rpmsg, true);
|
||||
}
|
||||
|
||||
static void pm_heart_beat_work_handler(struct work_struct *work)
|
||||
{
|
||||
struct pm_rpmsg_data msg;
|
||||
|
||||
/* Notify M4 side A7 in RUN mode at boot time */
|
||||
if (pm_rpmsg.first_flag) {
|
||||
pm_vlls_notify_m4(false);
|
||||
|
||||
pm_heartbeat_off_notify_m4(heartbeat_off);
|
||||
|
||||
pm_rpmsg.first_flag = false;
|
||||
}
|
||||
|
||||
if (!heartbeat_off) {
|
||||
msg.header.cate = IMX_RMPSG_LIFECYCLE;
|
||||
msg.header.major = IMX_RMPSG_MAJOR;
|
||||
msg.header.minor = IMX_RMPSG_MINOR;
|
||||
msg.header.type = HEATBEAT_RPMSG_TYPE;
|
||||
msg.header.cmd = PM_RPMSG_HEART_BEAT;
|
||||
msg.data = 0;
|
||||
pm_send_message(&msg, &pm_rpmsg, false);
|
||||
|
||||
schedule_delayed_work(&heart_beat_work,
|
||||
msecs_to_jiffies(30000));
|
||||
}
|
||||
}
|
||||
|
||||
static void pm_poweroff_rpmsg(void)
|
||||
{
|
||||
pm_shutdown_notify_m4();
|
||||
pr_emerg("Unable to poweroff system\n");
|
||||
}
|
||||
|
||||
static int pm_restart_handler(struct notifier_block *this, unsigned long mode,
|
||||
void *cmd)
|
||||
{
|
||||
pm_reboot_notify_m4();
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int pm_rpmsg_probe(struct rpmsg_device *rpdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pm_rpmsg.rpdev = rpdev;
|
||||
|
||||
dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
|
||||
rpdev->src, rpdev->dst);
|
||||
|
||||
init_completion(&pm_rpmsg.cmd_complete);
|
||||
mutex_init(&pm_rpmsg.lock);
|
||||
|
||||
INIT_DELAYED_WORK(&heart_beat_work,
|
||||
pm_heart_beat_work_handler);
|
||||
|
||||
pm_rpmsg.first_flag = true;
|
||||
schedule_delayed_work(&heart_beat_work, 0);
|
||||
|
||||
pm_rpmsg.restart_handler.notifier_call = pm_restart_handler;
|
||||
pm_rpmsg.restart_handler.priority = 128;
|
||||
ret = register_restart_handler(&pm_rpmsg.restart_handler);
|
||||
if (ret)
|
||||
dev_err(&rpdev->dev, "cannot register restart handler\n");
|
||||
|
||||
pm_power_off = pm_poweroff_rpmsg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
|
||||
void *priv, u32 src)
|
||||
{
|
||||
struct pm_rpmsg_data *msg = (struct pm_rpmsg_data *)data;
|
||||
|
||||
pm_rpmsg.msg = msg;
|
||||
|
||||
complete(&pm_rpmsg.cmd_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pm_rpmsg_remove(struct rpmsg_device *rpdev)
|
||||
{
|
||||
dev_info(&rpdev->dev, "pm rpmsg driver is removed\n");
|
||||
}
|
||||
|
||||
static struct rpmsg_device_id pm_rpmsg_id_table[] = {
|
||||
{ .name = "rpmsg-life-cycle-channel" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct rpmsg_driver pm_rpmsg_driver = {
|
||||
.drv.name = "pm_rpmsg",
|
||||
.drv.owner = THIS_MODULE,
|
||||
.id_table = pm_rpmsg_id_table,
|
||||
.probe = pm_rpmsg_probe,
|
||||
.callback = pm_rpmsg_cb,
|
||||
.remove = pm_rpmsg_remove,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pm_heartbeat_suspend(struct device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pm_vlls_notify_m4(true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cancel_delayed_work_sync(&heart_beat_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm_heartbeat_resume(struct device *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = pm_vlls_notify_m4(false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
schedule_delayed_work(&heart_beat_work,
|
||||
msecs_to_jiffies(10000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pm_heartbeat_probe(struct platform_device *pdev)
|
||||
{
|
||||
platform_set_drvdata(pdev, &pm_rpmsg);
|
||||
|
||||
return register_rpmsg_driver(&pm_rpmsg_driver);
|
||||
}
|
||||
|
||||
static const struct of_device_id pm_heartbeat_id[] = {
|
||||
{"fsl,heartbeat-rpmsg",},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pm_heartbeat_id);
|
||||
|
||||
static const struct dev_pm_ops pm_heartbeat_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_heartbeat_suspend,
|
||||
pm_heartbeat_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver pm_heartbeat_driver = {
|
||||
.driver = {
|
||||
.name = "heartbeat-rpmsg",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = pm_heartbeat_id,
|
||||
.pm = &pm_heartbeat_ops,
|
||||
},
|
||||
.probe = pm_heartbeat_probe,
|
||||
};
|
||||
|
||||
static int __init setup_heartbeat(char *str)
|
||||
{
|
||||
heartbeat_off = true;
|
||||
|
||||
return 1;
|
||||
};
|
||||
__setup("heartbeat_off", setup_heartbeat);
|
||||
|
||||
module_platform_driver(pm_heartbeat_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Freescale PM rpmsg driver");
|
||||
MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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 <asm/smp_scu.h>
|
||||
#include "hardware.h"
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.align 3
|
||||
|
||||
ENTRY(imx7_smp_wfe)
|
||||
push {r4 - r11, lr}
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
isb
|
||||
|
||||
/* Turn off SMP bit. */
|
||||
mrc p15, 0, r8, c1, c0, 1
|
||||
bic r8, r8, #0x40
|
||||
mcr p15, 0, r8, c1, c0, 1
|
||||
|
||||
isb
|
||||
/* Set flag of entering WFE. */
|
||||
mov r7, #0xff
|
||||
lsl r7, r7, r0
|
||||
mov r6, #SCU_PM_DORMANT
|
||||
lsl r6, r6, r0
|
||||
ldr r8, [r1, #0x4]
|
||||
bic r8, r8, r7
|
||||
orr r6, r6, r8
|
||||
str r6, [r1, #0x4]
|
||||
|
||||
go_back_wfe:
|
||||
wfe
|
||||
|
||||
/* Offset 0x0 stores busfeq done flag */
|
||||
ldr r6, [r1]
|
||||
cmp r6, #1
|
||||
beq go_back_wfe
|
||||
|
||||
/* Turn ON SMP bit. */
|
||||
mrc p15, 0, r8, c1, c0, 1
|
||||
orr r8, r8, #0x40
|
||||
mcr p15, 0, r8, c1, c0, 1
|
||||
|
||||
isb
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r8, c1, c0, 0
|
||||
orr r8, r8, #0x4
|
||||
mcr p15, 0, r8, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* Set flag of exiting WFE. */
|
||||
mov r7, #0xff
|
||||
lsl r7, r7, r0
|
||||
mov r6, #SCU_PM_NORMAL
|
||||
lsl r6, r6, r0
|
||||
ldr r8, [r1, #0x4]
|
||||
bic r8, r8, r7
|
||||
orr r6, r6, r8
|
||||
str r6, [r1, #0x4]
|
||||
|
||||
/* Pop all saved registers. */
|
||||
pop {r4 - r11, lr}
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
ENDPROC(imx7_smp_wfe)
|
||||
#endif
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/smp_scu.h>
|
||||
#include "hardware.h"
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.extern scu_base
|
||||
#endif
|
||||
|
||||
.globl wfe_smp_freq_change_start
|
||||
.globl wfe_smp_freq_change_end
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
.align 3
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r11, lr}
|
||||
|
||||
ldr r7, =v7_flush_kern_cache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r11, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
bic r6, r6, #0x4
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r11, lr}
|
||||
|
||||
ldr r7, =v7_flush_kern_cache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r11, lr}
|
||||
|
||||
.endm
|
||||
|
||||
ENTRY(wfe_smp_freq_change)
|
||||
wfe_smp_freq_change_start:
|
||||
push {r4 - r11, lr}
|
||||
|
||||
mov r6, r0
|
||||
mov r7, r1
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
isb
|
||||
|
||||
/* Turn off SMP bit. */
|
||||
mrc p15, 0, r8, c1, c0, 1
|
||||
bic r8, r8, #0x40
|
||||
mcr p15, 0, r8, c1, c0, 1
|
||||
|
||||
isb
|
||||
|
||||
/* Inform the SCU we are going to enter WFE. */
|
||||
push {r0 - r11, lr}
|
||||
|
||||
ldr r0,=scu_base
|
||||
ldr r0, [r0]
|
||||
mov r1, #SCU_PM_DORMANT
|
||||
ldr r3, =scu_power_mode
|
||||
mov lr, pc
|
||||
mov pc, r3
|
||||
|
||||
pop {r0 - r11, lr}
|
||||
|
||||
go_back_wfe:
|
||||
wfe
|
||||
|
||||
ldr r3, [r7]
|
||||
cmp r3, #1
|
||||
beq go_back_wfe
|
||||
|
||||
/* Turn ON SMP bit. */
|
||||
mrc p15, 0, r8, c1, c0, 1
|
||||
orr r8, r8, #0x40
|
||||
mcr p15, 0, r8, c1, c0, 1
|
||||
|
||||
isb
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r8, c1, c0, 0
|
||||
orr r8, r8, #0x4
|
||||
mcr p15, 0, r8, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* Inform the SCU we have exited WFE. */
|
||||
push {r0 - r11, lr}
|
||||
|
||||
ldr r0,=scu_base
|
||||
ldr r0, [r0]
|
||||
mov r1, #SCU_PM_NORMAL
|
||||
ldr r3, =scu_power_mode
|
||||
mov lr, pc
|
||||
mov pc, r3
|
||||
|
||||
pop {r0 - r11, lr}
|
||||
|
||||
/* Pop all saved registers. */
|
||||
pop {r4 - r11, lr}
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
wfe_smp_freq_change_end:
|
||||
ENDPROC(wfe_smp_freq_change)
|
||||
|
||||
#ifdef CONFIG_OPTEE
|
||||
/**
|
||||
* @brief Switch CPU in WFE mode while bus frequency change
|
||||
* on-going
|
||||
*
|
||||
* @param[in] r0 CPU in WFE Status
|
||||
* @param[in] r1 Bus frequency change status
|
||||
*/
|
||||
|
||||
.globl imx_smp_wfe_optee_end
|
||||
|
||||
ENTRY(imx_smp_wfe_optee)
|
||||
push {r4-r11, lr}
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
disable_l1_dcache
|
||||
isb
|
||||
|
||||
/* Set flag CPU entering WFE. */
|
||||
mov r4, #1
|
||||
str r4, [r0]
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
1:
|
||||
wfe
|
||||
|
||||
/* Check if busfreq is done, else loop */
|
||||
ldr r4, [r1]
|
||||
cmp r4, #1
|
||||
beq 1b
|
||||
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r4, c1, c0, 0
|
||||
orr r4, r4, #0x4
|
||||
mcr p15, 0, r4, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* Set flag CPU exiting WFE. */
|
||||
mov r4, #0
|
||||
str r4, [r0]
|
||||
|
||||
/* Pop all saved registers. */
|
||||
pop {r4-r11, lr}
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
imx_smp_wfe_optee_end:
|
||||
ENDPROC(imx_smp_wfe_optee)
|
||||
#endif
|
||||
#endif
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/smp.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include "common.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define SRC_SCR 0x000
|
||||
#define SRC_GPR1 0x020
|
||||
|
@ -23,9 +24,18 @@
|
|||
#define BP_SRC_SCR_SW_IPU2_RST 12
|
||||
#define BP_SRC_SCR_CORE1_RST 14
|
||||
#define BP_SRC_SCR_CORE1_ENABLE 22
|
||||
/* below is for i.MX7D */
|
||||
#define SRC_GPR1_V2 0x074
|
||||
#define SRC_A7RCR0 0x004
|
||||
#define SRC_A7RCR1 0x008
|
||||
#define SRC_M4RCR 0x00C
|
||||
|
||||
#define BP_SRC_A7RCR0_A7_CORE_RESET0 0
|
||||
#define BP_SRC_A7RCR1_A7_CORE1_ENABLE 1
|
||||
|
||||
static void __iomem *src_base;
|
||||
static DEFINE_SPINLOCK(scr_lock);
|
||||
static DEFINE_SPINLOCK(src_lock);
|
||||
static bool m4_is_enabled;
|
||||
|
||||
static const int sw_reset_bits[5] = {
|
||||
BP_SRC_SCR_SW_GPU_RST,
|
||||
|
@ -35,6 +45,11 @@ static const int sw_reset_bits[5] = {
|
|||
BP_SRC_SCR_SW_IPU2_RST
|
||||
};
|
||||
|
||||
bool imx_src_is_m4_enabled(void)
|
||||
{
|
||||
return m4_is_enabled;
|
||||
}
|
||||
|
||||
static int imx_src_reset_module(struct reset_controller_dev *rcdev,
|
||||
unsigned long sw_reset_idx)
|
||||
{
|
||||
|
@ -51,11 +66,11 @@ static int imx_src_reset_module(struct reset_controller_dev *rcdev,
|
|||
|
||||
bit = 1 << sw_reset_bits[sw_reset_idx];
|
||||
|
||||
spin_lock_irqsave(&scr_lock, flags);
|
||||
spin_lock_irqsave(&src_lock, flags);
|
||||
val = readl_relaxed(src_base + SRC_SCR);
|
||||
val |= bit;
|
||||
writel_relaxed(val, src_base + SRC_SCR);
|
||||
spin_unlock_irqrestore(&scr_lock, flags);
|
||||
spin_unlock_irqrestore(&src_lock, flags);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
while (readl(src_base + SRC_SCR) & bit) {
|
||||
|
@ -81,32 +96,59 @@ void imx_enable_cpu(int cpu, bool enable)
|
|||
u32 mask, val;
|
||||
|
||||
cpu = cpu_logical_map(cpu);
|
||||
mask = 1 << (BP_SRC_SCR_CORE1_ENABLE + cpu - 1);
|
||||
spin_lock(&scr_lock);
|
||||
val = readl_relaxed(src_base + SRC_SCR);
|
||||
val = enable ? val | mask : val & ~mask;
|
||||
val |= 1 << (BP_SRC_SCR_CORE1_RST + cpu - 1);
|
||||
writel_relaxed(val, src_base + SRC_SCR);
|
||||
spin_unlock(&scr_lock);
|
||||
spin_lock(&src_lock);
|
||||
if (cpu_is_imx7d()) {
|
||||
/* enable core */
|
||||
if (enable)
|
||||
imx_gpcv2_set_core1_pdn_pup_by_software(false);
|
||||
|
||||
mask = 1 << (BP_SRC_A7RCR1_A7_CORE1_ENABLE + cpu - 1);
|
||||
val = readl_relaxed(src_base + SRC_A7RCR1);
|
||||
val = enable ? val | mask : val & ~mask;
|
||||
writel_relaxed(val, src_base + SRC_A7RCR1);
|
||||
} else {
|
||||
mask = 1 << (BP_SRC_SCR_CORE1_ENABLE + cpu - 1);
|
||||
val = readl_relaxed(src_base + SRC_SCR);
|
||||
val = enable ? val | mask : val & ~mask;
|
||||
val |= 1 << (BP_SRC_SCR_CORE1_RST + cpu - 1);
|
||||
writel_relaxed(val, src_base + SRC_SCR);
|
||||
}
|
||||
spin_unlock(&src_lock);
|
||||
}
|
||||
|
||||
void imx_set_cpu_jump(int cpu, void *jump_addr)
|
||||
{
|
||||
spin_lock(&src_lock);
|
||||
cpu = cpu_logical_map(cpu);
|
||||
writel_relaxed(__pa_symbol(jump_addr),
|
||||
src_base + SRC_GPR1 + cpu * 8);
|
||||
if (cpu_is_imx7d())
|
||||
writel_relaxed(__pa_symbol(jump_addr),
|
||||
src_base + SRC_GPR1_V2 + cpu * 8);
|
||||
else
|
||||
writel_relaxed(__pa_symbol(jump_addr),
|
||||
src_base + SRC_GPR1 + cpu * 8);
|
||||
spin_unlock(&src_lock);
|
||||
}
|
||||
|
||||
u32 imx_get_cpu_arg(int cpu)
|
||||
{
|
||||
cpu = cpu_logical_map(cpu);
|
||||
return readl_relaxed(src_base + SRC_GPR1 + cpu * 8 + 4);
|
||||
if (cpu_is_imx7d())
|
||||
return readl_relaxed(src_base + SRC_GPR1_V2
|
||||
+ cpu * 8 + 4);
|
||||
else
|
||||
return readl_relaxed(src_base + SRC_GPR1
|
||||
+ cpu * 8 + 4);
|
||||
}
|
||||
|
||||
void imx_set_cpu_arg(int cpu, u32 arg)
|
||||
{
|
||||
cpu = cpu_logical_map(cpu);
|
||||
writel_relaxed(arg, src_base + SRC_GPR1 + cpu * 8 + 4);
|
||||
if (cpu_is_imx7d())
|
||||
writel_relaxed(arg, src_base + SRC_GPR1_V2
|
||||
+ cpu * 8 + 4);
|
||||
else
|
||||
writel_relaxed(arg, src_base + SRC_GPR1
|
||||
+ cpu * 8 + 4);
|
||||
}
|
||||
|
||||
void __init imx_src_init(void)
|
||||
|
@ -120,6 +162,15 @@ void __init imx_src_init(void)
|
|||
src_base = of_iomap(np, 0);
|
||||
WARN_ON(!src_base);
|
||||
|
||||
if (cpu_is_imx7d()) {
|
||||
val = readl_relaxed(src_base + SRC_M4RCR);
|
||||
if (((val & BIT(3)) == BIT(3)) && !(val & BIT(0)))
|
||||
m4_is_enabled = true;
|
||||
else
|
||||
m4_is_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
imx_reset_controller.of_node = np;
|
||||
if (IS_ENABLED(CONFIG_RESET_CONTROLLER))
|
||||
reset_controller_register(&imx_reset_controller);
|
||||
|
@ -128,9 +179,17 @@ void __init imx_src_init(void)
|
|||
* force warm reset sources to generate cold reset
|
||||
* for a more reliable restart
|
||||
*/
|
||||
spin_lock(&scr_lock);
|
||||
spin_lock(&src_lock);
|
||||
val = readl_relaxed(src_base + SRC_SCR);
|
||||
|
||||
/* bit 4 is m4c_non_sclr_rst on i.MX6SX */
|
||||
if (cpu_is_imx6sx() && ((val &
|
||||
(1 << BP_SRC_SCR_SW_OPEN_VG_RST)) == 0))
|
||||
m4_is_enabled = true;
|
||||
else
|
||||
m4_is_enabled = false;
|
||||
|
||||
val &= ~(1 << BP_SRC_SCR_WARM_RESET_ENABLE);
|
||||
writel_relaxed(val, src_base + SRC_SCR);
|
||||
spin_unlock(&scr_lock);
|
||||
spin_unlock(&src_lock);
|
||||
}
|
||||
|
|
|
@ -41,23 +41,32 @@
|
|||
#define PM_INFO_RESUME_ADDR_OFFSET 0x4
|
||||
#define PM_INFO_DDR_TYPE_OFFSET 0x8
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0xC
|
||||
#define PM_INFO_MX6Q_MMDC_P_OFFSET 0x10
|
||||
#define PM_INFO_MX6Q_MMDC_V_OFFSET 0x14
|
||||
#define PM_INFO_MX6Q_SRC_P_OFFSET 0x18
|
||||
#define PM_INFO_MX6Q_SRC_V_OFFSET 0x1C
|
||||
#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x20
|
||||
#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x24
|
||||
#define PM_INFO_MX6Q_CCM_P_OFFSET 0x28
|
||||
#define PM_INFO_MX6Q_CCM_V_OFFSET 0x2C
|
||||
#define PM_INFO_MX6Q_GPC_P_OFFSET 0x30
|
||||
#define PM_INFO_MX6Q_GPC_V_OFFSET 0x34
|
||||
#define PM_INFO_MX6Q_L2_P_OFFSET 0x38
|
||||
#define PM_INFO_MX6Q_L2_V_OFFSET 0x3C
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x40
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x44
|
||||
#define PM_INFO_MX6Q_MMDC0_P_OFFSET 0x10
|
||||
#define PM_INFO_MX6Q_MMDC0_V_OFFSET 0x14
|
||||
#define PM_INFO_MX6Q_MMDC1_P_OFFSET 0x18
|
||||
#define PM_INFO_MX6Q_MMDC1_V_OFFSET 0x1C
|
||||
#define PM_INFO_MX6Q_SRC_P_OFFSET 0x20
|
||||
#define PM_INFO_MX6Q_SRC_V_OFFSET 0x24
|
||||
#define PM_INFO_MX6Q_IOMUXC_P_OFFSET 0x28
|
||||
#define PM_INFO_MX6Q_IOMUXC_V_OFFSET 0x2C
|
||||
#define PM_INFO_MX6Q_CCM_P_OFFSET 0x30
|
||||
#define PM_INFO_MX6Q_CCM_V_OFFSET 0x34
|
||||
#define PM_INFO_MX6Q_GPC_P_OFFSET 0x38
|
||||
#define PM_INFO_MX6Q_GPC_V_OFFSET 0x3C
|
||||
#define PM_INFO_MX6Q_L2_P_OFFSET 0x40
|
||||
#define PM_INFO_MX6Q_L2_V_OFFSET 0x44
|
||||
#define PM_INFO_MX6Q_ANATOP_P_OFFSET 0x48
|
||||
#define PM_INFO_MX6Q_ANATOP_V_OFFSET 0x4C
|
||||
#define PM_INFO_MX6Q_TTBR1_V_OFFSET 0x50
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x54
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x58
|
||||
/* below offsets depends on MX6_MAX_MMDC_IO_NUM(36) definition */
|
||||
#define PM_INFO_MMDC_NUM_OFFSET 0x208
|
||||
#define PM_INFO_MMDC_VAL_OFFSET 0x20C
|
||||
|
||||
#define MX6Q_SRC_GPR1 0x20
|
||||
#define MX6Q_SRC_GPR2 0x24
|
||||
#define MX6Q_MMDC_MISC 0x18
|
||||
#define MX6Q_MMDC_MAPSR 0x404
|
||||
#define MX6Q_MMDC_MPDGCTRL0 0x83c
|
||||
#define MX6Q_GPC_IMR1 0x08
|
||||
|
@ -65,9 +74,49 @@
|
|||
#define MX6Q_GPC_IMR3 0x10
|
||||
#define MX6Q_GPC_IMR4 0x14
|
||||
#define MX6Q_CCM_CCR 0x0
|
||||
#define MX6Q_ANATOP_CORE 0x140
|
||||
|
||||
.align 3
|
||||
|
||||
/* Check if the cpu is cortex-a7 */
|
||||
.macro is_cortex_a7
|
||||
|
||||
/* Read the primary cpu number is MPIDR */
|
||||
mrc p15, 0, r5, c0, c0, 0
|
||||
ldr r6, =0xfff0
|
||||
and r5, r5, r6
|
||||
ldr r6, =0xc070
|
||||
cmp r5, r6
|
||||
|
||||
.endm
|
||||
|
||||
.macro disable_l1_cache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 -r10, lr}
|
||||
ldr r7, = v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc , r7
|
||||
pop {r0 -r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro sync_l2_cache
|
||||
|
||||
/* sync L2 cache to drain L2's buffers to DRAM. */
|
||||
|
@ -86,29 +135,8 @@
|
|||
|
||||
.endm
|
||||
|
||||
.macro resume_mmdc
|
||||
|
||||
/* restore MMDC IO */
|
||||
cmp r5, #0x0
|
||||
ldreq r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldrne r11, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
|
||||
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
1:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r11, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 1b
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
ldrne r11, [r0, #PM_INFO_MX6Q_MMDC_P_OFFSET]
|
||||
|
||||
cmp r3, #IMX_DDR_TYPE_LPDDR2
|
||||
bne 4f
|
||||
/* r11 must be MMDC base address */
|
||||
.macro reset_read_fifo
|
||||
|
||||
/* reset read FIFO, RST_RD_FIFO */
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
|
@ -128,23 +156,294 @@
|
|||
ldr r6, [r11, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 3b
|
||||
|
||||
/* check if lppdr2 2 channel mode is enabled */
|
||||
ldr r7, =MX6Q_MMDC_MISC
|
||||
ldr r6, [r11, r7]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq 6f
|
||||
|
||||
ldr r7, =MX6Q_MMDC_MPDGCTRL0
|
||||
ldr r6, [r12, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r12, r7]
|
||||
4:
|
||||
ldr r6, [r12, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 4b
|
||||
|
||||
ldr r6, [r12, r7]
|
||||
orr r6, r6, #(1 << 31)
|
||||
str r6, [r12, r7]
|
||||
5:
|
||||
ldr r6, [r12, r7]
|
||||
ands r6, r6, #(1 << 31)
|
||||
bne 5b
|
||||
|
||||
6:
|
||||
.endm
|
||||
|
||||
/* r11 must be MMDC base address */
|
||||
.macro mmdc_out_and_auto_self_refresh
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 21)
|
||||
str r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
5:
|
||||
7:
|
||||
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
bne 5b
|
||||
bne 7b
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
/* check if lppdr2 2 channel mode is enabled */
|
||||
ldr r7, =MX6Q_MMDC_MISC
|
||||
ldr r6, [r11, r7]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq 9f
|
||||
|
||||
ldr r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 21)
|
||||
str r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
8:
|
||||
ldr r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
bne 8b
|
||||
|
||||
ldr r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
9:
|
||||
.endm
|
||||
|
||||
/* r10 must be iomuxc base address */
|
||||
.macro resume_iomuxc_gpr
|
||||
|
||||
add r10, r10, #0x4000
|
||||
/* IOMUXC GPR DRAM_RESET_BYPASS */
|
||||
ldr r4, [r10, #0x8]
|
||||
bic r4, r4, #(0x1 << 27)
|
||||
str r4, [r10, #0x8]
|
||||
/* IOMUXC GPR DRAM_CKE_BYPASS */
|
||||
ldr r4, [r10, #0x8]
|
||||
bic r4, r4, #(0x1 << 31)
|
||||
str r4, [r10, #0x8]
|
||||
|
||||
.endm
|
||||
|
||||
.macro resume_io
|
||||
|
||||
/* restore MMDC IO */
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
|
||||
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
10:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x8
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 10b
|
||||
|
||||
cmp r5, #0x0
|
||||
/* Here only MMDC0 is set */
|
||||
ldreq r11, [r0, #PM_INFO_MX6Q_MMDC0_V_OFFSET]
|
||||
ldrne r11, [r0, #PM_INFO_MX6Q_MMDC0_P_OFFSET]
|
||||
ldreq r12, [r0, #PM_INFO_MX6Q_MMDC1_V_OFFSET]
|
||||
ldrne r12, [r0, #PM_INFO_MX6Q_MMDC1_P_OFFSET]
|
||||
|
||||
reset_read_fifo
|
||||
mmdc_out_and_auto_self_refresh
|
||||
|
||||
.endm
|
||||
|
||||
.macro resume_mmdc_io
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r10, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX6Q_IOMUXC_P_OFFSET]
|
||||
ldreq r11, [r0, #PM_INFO_MX6Q_MMDC0_V_OFFSET]
|
||||
ldrne r11, [r0, #PM_INFO_MX6Q_MMDC0_P_OFFSET]
|
||||
|
||||
/* resume mmdc iomuxc settings */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
11:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x8
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 11b
|
||||
|
||||
/* check whether we need to restore MMDC */
|
||||
cmp r5, #0x0
|
||||
beq 12f
|
||||
|
||||
/* check whether last suspend is with M/F mix off */
|
||||
ldr r9, [r0, #PM_INFO_MX6Q_GPC_P_OFFSET]
|
||||
ldr r6, [r9, #0x220]
|
||||
cmp r6, #0x0
|
||||
bne 13f
|
||||
12:
|
||||
resume_iomuxc_gpr
|
||||
reset_read_fifo
|
||||
|
||||
b 17f
|
||||
13:
|
||||
/* restore MMDC settings */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
14:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r11, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 14b
|
||||
|
||||
/* let DDR enter self-refresh */
|
||||
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
15:
|
||||
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq 15b
|
||||
|
||||
resume_iomuxc_gpr
|
||||
reset_read_fifo
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
16:
|
||||
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
bne 16b
|
||||
|
||||
/* kick off MMDC */
|
||||
ldr r4, =0x0
|
||||
str r4, [r11, #0x1c]
|
||||
|
||||
17:
|
||||
mmdc_out_and_auto_self_refresh
|
||||
|
||||
.endm
|
||||
|
||||
.macro store_ttbr1
|
||||
|
||||
/* Store TTBR1 to pm_info->ttbr1 */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_MX6Q_TTBR1_V_OFFSET]
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the BTAC. */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
ldr r6, =iram_tlb_phys_addr
|
||||
ldr r6, [r6]
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Store the IRAM table in TTBR1 */
|
||||
mcr p15, 0, r6, 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_cortex_a7
|
||||
beq 17f
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
ldr r8, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
|
||||
mov r6, #0x0
|
||||
str r6, [r8, #0x100]
|
||||
|
||||
dsb
|
||||
isb
|
||||
#endif
|
||||
17:
|
||||
.endm
|
||||
|
||||
.macro restore_ttbr1
|
||||
|
||||
is_cortex_a7
|
||||
beq 18f
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* Enable L2. */
|
||||
ldr r8, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
|
||||
ldr r7, =0x1
|
||||
str r7, [r8, #0x100]
|
||||
#endif
|
||||
|
||||
18:
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
orr r6, r6, #0x4
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Restore TTBCR */
|
||||
/* 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
|
||||
|
||||
/* 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
|
||||
|
||||
/* Restore TTBR1, get the origin ttbr1 from pm info */
|
||||
ldr r7, [r0, #PM_INFO_MX6Q_TTBR1_V_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
|
||||
ENTRY(imx6_suspend)
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
|
@ -179,10 +478,25 @@ ENTRY(imx6_suspend)
|
|||
str r9, [r11, #MX6Q_SRC_GPR1]
|
||||
str r1, [r11, #MX6Q_SRC_GPR2]
|
||||
|
||||
/*
|
||||
* Check if the cpu is Cortex-A7, for Cortex-A7
|
||||
* the cache implementation is not the same as
|
||||
* Cortex-A9, so the cache maintenance operation
|
||||
* is different.
|
||||
*/
|
||||
is_cortex_a7
|
||||
beq a7_dache_flush
|
||||
|
||||
/* need to sync L2 cache before DSM. */
|
||||
sync_l2_cache
|
||||
b ttbr_store
|
||||
a7_dache_flush:
|
||||
disable_l1_cache
|
||||
ttbr_store:
|
||||
store_ttbr1
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
|
||||
ldr r11, [r0, #PM_INFO_MX6Q_MMDC0_V_OFFSET]
|
||||
ldr r12, [r0, #PM_INFO_MX6Q_MMDC1_V_OFFSET]
|
||||
/*
|
||||
* put DDR explicitly into self-refresh and
|
||||
* disable automatic power savings.
|
||||
|
@ -201,31 +515,59 @@ poll_dvfs_set:
|
|||
ands r7, r7, #(1 << 25)
|
||||
beq poll_dvfs_set
|
||||
|
||||
/* check if lppdr2 2 channel mode is enabled */
|
||||
ldr r7, =MX6Q_MMDC_MISC
|
||||
ldr r6, [r11, r7]
|
||||
ands r6, r6, #(1 << 2)
|
||||
beq skip_self_refresh_ch1
|
||||
|
||||
ldr r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
ldr r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 21)
|
||||
str r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
|
||||
poll_dvfs_set_ch1:
|
||||
ldr r7, [r12, #MX6Q_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 25)
|
||||
beq poll_dvfs_set_ch1
|
||||
|
||||
skip_self_refresh_ch1:
|
||||
/* use r11 to store the IO address */
|
||||
ldr r11, [r0, #PM_INFO_MX6Q_IOMUXC_V_OFFSET]
|
||||
ldr r6, =0x0
|
||||
ldr r7, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r8, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r8, r8, r0
|
||||
/* LPDDR2's last 3 IOs need special setting */
|
||||
cmp r3, #IMX_DDR_TYPE_LPDDR2
|
||||
subeq r7, r7, #0x3
|
||||
set_mmdc_io_lpm:
|
||||
ldr r9, [r8], #0x8
|
||||
str r6, [r11, r9]
|
||||
subs r7, r7, #0x1
|
||||
ldr r7, [r8], #0x8
|
||||
ldr r9, [r8], #0x4
|
||||
str r9, [r11, r7]
|
||||
subs r6, r6, #0x1
|
||||
bne set_mmdc_io_lpm
|
||||
|
||||
cmp r3, #IMX_DDR_TYPE_LPDDR2
|
||||
bne set_mmdc_io_lpm_done
|
||||
ldr r6, =0x1000
|
||||
ldr r9, [r8], #0x8
|
||||
str r6, [r11, r9]
|
||||
ldr r9, [r8], #0x8
|
||||
str r6, [r11, r9]
|
||||
ldr r6, =0x80000
|
||||
ldr r9, [r8]
|
||||
str r6, [r11, r9]
|
||||
set_mmdc_io_lpm_done:
|
||||
/* check whether it supports Mega/Fast off */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
|
||||
cmp r6, #0x0
|
||||
beq set_mmdc_lpm_done
|
||||
|
||||
/* IOMUXC GPR DRAM_RESET */
|
||||
add r11, r11, #0x4000
|
||||
ldr r6, [r11, #0x8]
|
||||
orr r6, r6, #(0x1 << 28)
|
||||
str r6, [r11, #0x8]
|
||||
|
||||
/* IOMUXC GPR DRAM_RESET_BYPASS */
|
||||
ldr r6, [r11, #0x8]
|
||||
orr r6, r6, #(0x1 << 27)
|
||||
str r6, [r11, #0x8]
|
||||
|
||||
/* IOMUXC GPR DRAM_CKE_BYPASS */
|
||||
ldr r6, [r11, #0x8]
|
||||
orr r6, r6, #(0x1 << 31)
|
||||
str r6, [r11, #0x8]
|
||||
set_mmdc_lpm_done:
|
||||
|
||||
/*
|
||||
* mask all GPC interrupts before
|
||||
|
@ -285,6 +627,27 @@ rbc_loop:
|
|||
subs r6, r6, #0x1
|
||||
bne rbc_loop
|
||||
|
||||
/*
|
||||
* ERR005852 Analog: Transition from Deep Sleep Mode to
|
||||
* LDO Bypass Mode may cause the slow response of the
|
||||
* VDDARM_CAP output.
|
||||
*
|
||||
* Software workaround:
|
||||
* if internal ldo(VDDARM) bypassed, switch to analog bypass
|
||||
* mode (0x1E), prio to entering DSM, and then, revert to the
|
||||
* normal bypass mode, when exiting from DSM.
|
||||
*/
|
||||
ldr r11, [r0, #PM_INFO_MX6Q_ANATOP_V_OFFSET]
|
||||
ldr r10, [r11, #MX6Q_ANATOP_CORE]
|
||||
and r10, r10, #0x1f
|
||||
cmp r10, #0x1f
|
||||
bne ldo_check_done1
|
||||
ldo_analog_bypass:
|
||||
ldr r10, [r11, #MX6Q_ANATOP_CORE]
|
||||
bic r10, r10, #0x1f
|
||||
orr r10, r10, #0x1e
|
||||
str r10, [r11, #MX6Q_ANATOP_CORE]
|
||||
ldo_check_done1:
|
||||
/* Zzz, enter stop mode */
|
||||
wfi
|
||||
nop
|
||||
|
@ -297,8 +660,28 @@ rbc_loop:
|
|||
* wakeup source, system should auto
|
||||
* resume, we need to restore MMDC IO first
|
||||
*/
|
||||
/* restore it with 0x1f if use ldo bypass mode.*/
|
||||
ldr r10, [r11, #MX6Q_ANATOP_CORE]
|
||||
and r10, r10, #0x1f
|
||||
cmp r10, #0x1e
|
||||
bne ldo_check_done2
|
||||
ldo_bypass_restore:
|
||||
ldr r10, [r11, #MX6Q_ANATOP_CORE]
|
||||
orr r10, r10, #0x1f
|
||||
str r10, [r11, #MX6Q_ANATOP_CORE]
|
||||
ldo_check_done2:
|
||||
mov r5, #0x0
|
||||
resume_mmdc
|
||||
/* check whether it supports Mega/Fast off */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
|
||||
cmp r6, #0x0
|
||||
beq only_resume_io
|
||||
resume_mmdc_io
|
||||
b resume_mmdc_done
|
||||
only_resume_io:
|
||||
resume_io
|
||||
resume_mmdc_done:
|
||||
|
||||
restore_ttbr1
|
||||
|
||||
/* return to suspend finish */
|
||||
ret lr
|
||||
|
@ -313,6 +696,16 @@ resume:
|
|||
mcr p15, 0, r6, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* restore it with 0x1f if use ldo bypass mode.*/
|
||||
ldr r11, [r0, #PM_INFO_MX6Q_ANATOP_P_OFFSET]
|
||||
ldr r7, [r11, #MX6Q_ANATOP_CORE]
|
||||
and r7, r7, #0x1f
|
||||
cmp r7, #0x1e
|
||||
bne ldo_check_done3
|
||||
ldr r7, [r11, #MX6Q_ANATOP_CORE]
|
||||
orr r7, r7, #0x1f
|
||||
str r7, [r11, #MX6Q_ANATOP_CORE]
|
||||
ldo_check_done3:
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
/* clear core0's entry and parameter */
|
||||
|
@ -323,7 +716,16 @@ resume:
|
|||
|
||||
ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
|
||||
mov r5, #0x1
|
||||
resume_mmdc
|
||||
/* check whether it supports Mega/Fast off */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
|
||||
cmp r6, #0x0
|
||||
beq dsm_only_resume_io
|
||||
resume_mmdc_io
|
||||
b dsm_resume_mmdc_done
|
||||
dsm_only_resume_io:
|
||||
ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
|
||||
resume_io
|
||||
dsm_resume_mmdc_done:
|
||||
|
||||
ret lr
|
||||
ENDPROC(imx6_suspend)
|
||||
|
@ -336,8 +738,11 @@ ENDPROC(imx6_suspend)
|
|||
|
||||
ENTRY(v7_cpu_resume)
|
||||
bl v7_invalidate_l1
|
||||
is_cortex_a7
|
||||
beq done
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
bl l2c310_early_resume
|
||||
#endif
|
||||
done:
|
||||
b cpu_resume
|
||||
ENDPROC(v7_cpu_resume)
|
||||
|
|
|
@ -0,0 +1,714 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include "hardware.h"
|
||||
|
||||
/*
|
||||
* ==================== low level suspend ====================
|
||||
*
|
||||
* Better to follow below rules to use ARM registers:
|
||||
* r0: pm_info structure address;
|
||||
* r1 ~ r4: for saving pm_info members;
|
||||
* r5 ~ r10: free registers;
|
||||
* r11: io base address.
|
||||
*
|
||||
* suspend ocram space layout:
|
||||
* ======================== high address ======================
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* ^
|
||||
* ^
|
||||
* ^
|
||||
* imx7_suspend code
|
||||
* PM_INFO structure(imx7_cpu_pm_info)
|
||||
* ======================== low address =======================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Below offsets are based on struct imx7_cpu_pm_info
|
||||
* which defined in arch/arm/mach-imx/pm-imx7.c, this
|
||||
* structure contains necessary pm info for low level
|
||||
* suspend related code.
|
||||
*/
|
||||
#define PM_INFO_M4_RESERVE0_OFFSET 0x0
|
||||
#define PM_INFO_M4_RESERVE1_OFFSET 0x4
|
||||
#define PM_INFO_M4_RESERVE2_OFFSET 0x8
|
||||
#define PM_INFO_PBASE_OFFSET 0xc
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x10
|
||||
#define PM_INFO_DDR_TYPE_OFFSET 0x14
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x18
|
||||
#define PM_INFO_MX7_DDRC_P_OFFSET 0x1c
|
||||
#define PM_INFO_MX7_DDRC_V_OFFSET 0x20
|
||||
#define PM_INFO_MX7_DDRC_PHY_P_OFFSET 0x24
|
||||
#define PM_INFO_MX7_DDRC_PHY_V_OFFSET 0x28
|
||||
#define PM_INFO_MX7_SRC_P_OFFSET 0x2c
|
||||
#define PM_INFO_MX7_SRC_V_OFFSET 0x30
|
||||
#define PM_INFO_MX7_IOMUXC_GPR_P_OFFSET 0x34
|
||||
#define PM_INFO_MX7_IOMUXC_GPR_V_OFFSET 0x38
|
||||
#define PM_INFO_MX7_CCM_P_OFFSET 0x3c
|
||||
#define PM_INFO_MX7_CCM_V_OFFSET 0x40
|
||||
#define PM_INFO_MX7_GPC_P_OFFSET 0x44
|
||||
#define PM_INFO_MX7_GPC_V_OFFSET 0x48
|
||||
#define PM_INFO_MX7_SNVS_P_OFFSET 0x4c
|
||||
#define PM_INFO_MX7_SNVS_V_OFFSET 0x50
|
||||
#define PM_INFO_MX7_ANATOP_P_OFFSET 0x54
|
||||
#define PM_INFO_MX7_ANATOP_V_OFFSET 0x58
|
||||
#define PM_INFO_MX7_LPSR_P_OFFSET 0x5c
|
||||
#define PM_INFO_MX7_LPSR_V_OFFSET 0x60
|
||||
#define PM_INFO_MX7_GIC_DIST_P_OFFSET 0x64
|
||||
#define PM_INFO_MX7_GIC_DIST_V_OFFSET 0x68
|
||||
#define PM_INFO_MX7_TTBR1_V_OFFSET 0x6c
|
||||
#define PM_INFO_DDRC_REG_NUM_OFFSET 0x70
|
||||
#define PM_INFO_DDRC_REG_OFFSET 0x74
|
||||
#define PM_INFO_DDRC_VALUE_OFFSET 0x78
|
||||
#define PM_INFO_DDRC_PHY_REG_NUM_OFFSET 0x174
|
||||
#define PM_INFO_DDRC_PHY_REG_OFFSET 0x178
|
||||
#define PM_INFO_DDRC_PHY_VALUE_OFFSET 0x17c
|
||||
|
||||
#define MX7_SRC_GPR1 0x74
|
||||
#define MX7_SRC_GPR2 0x78
|
||||
#define GPC_PGC_C0 0x800
|
||||
#define GPC_PGC_FM 0xa00
|
||||
#define ANADIG_SNVS_MISC_CTRL 0x380
|
||||
#define ANADIG_SNVS_MISC_CTRL_SET 0x384
|
||||
#define ANADIG_SNVS_MISC_CTRL_CLR 0x388
|
||||
#define ANADIG_DIGPROG 0x800
|
||||
#define DDRC_STAT 0x4
|
||||
#define DDRC_PWRCTL 0x30
|
||||
#define DDRC_PSTAT 0x3fc
|
||||
#define DDRC_PCTRL_0 0x490
|
||||
#define DDRC_DFIMISC 0x1b0
|
||||
#define DDRC_SWCTL 0x320
|
||||
#define DDRC_SWSTAT 0x324
|
||||
#define DDRPHY_LP_CON0 0x18
|
||||
|
||||
#define CCM_SNVS_LPCG 0x250
|
||||
#define MX7D_GPC_IMR1 0x30
|
||||
#define MX7D_GPC_IMR2 0x34
|
||||
#define MX7D_GPC_IMR3 0x38
|
||||
#define MX7D_GPC_IMR4 0x3c
|
||||
|
||||
.align 3
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro store_ttbr1
|
||||
|
||||
/* Store TTBR1 to pm_info->ttbr1 */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_MX7_TTBR1_V_OFFSET]
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the BTAC. */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
ldr r6, =iram_tlb_phys_addr
|
||||
ldr r6, [r6]
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Store the IRAM table in TTBR1 */
|
||||
mcr p15, 0, r6, 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
|
||||
|
||||
.endm
|
||||
|
||||
.macro restore_ttbr1
|
||||
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
orr r6, r6, #0x4
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Restore TTBCR */
|
||||
/* 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
|
||||
|
||||
/* 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
|
||||
|
||||
/* Restore TTBR1, get the origin ttbr1 from pm info */
|
||||
ldr r7, [r0, #PM_INFO_MX7_TTBR1_V_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.macro ddrc_enter_self_refresh
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, =0x0
|
||||
str r7, [r11, #DDRC_PWRCTL]
|
||||
|
||||
/* wait rw port_busy clear */
|
||||
ldr r6, =(0x1 << 16)
|
||||
orr r6, r6, #0x1
|
||||
1:
|
||||
ldr r7, [r11, #DDRC_PSTAT]
|
||||
ands r7, r7, r6
|
||||
bne 1b
|
||||
|
||||
/* enter self-refresh bit 5 */
|
||||
ldr r7, =(0x1 << 5)
|
||||
str r7, [r11, #DDRC_PWRCTL]
|
||||
|
||||
/* wait until self-refresh mode entered */
|
||||
2:
|
||||
ldr r7, [r11, #DDRC_STAT]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x3
|
||||
bne 2b
|
||||
3:
|
||||
ldr r7, [r11, #DDRC_STAT]
|
||||
ands r7, r7, #0x20
|
||||
beq 3b
|
||||
|
||||
/* disable dram clk */
|
||||
ldr r7, [r11, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 3)
|
||||
str r7, [r11, #DDRC_PWRCTL]
|
||||
|
||||
.endm
|
||||
|
||||
.macro ddrc_exit_self_refresh
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
|
||||
ldrne r11, [r0, #PM_INFO_MX7_DDRC_P_OFFSET]
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, =0x0
|
||||
str r7, [r11, #DDRC_PWRCTL]
|
||||
|
||||
/* wait until self-refresh mode entered */
|
||||
4:
|
||||
ldr r7, [r11, #DDRC_STAT]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x3
|
||||
beq 4b
|
||||
|
||||
/* enable auto self-refresh */
|
||||
ldr r7, [r11, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 0)
|
||||
str r7, [r11, #DDRC_PWRCTL]
|
||||
|
||||
.endm
|
||||
|
||||
.macro wait_delay
|
||||
5:
|
||||
subs r6, r6, #0x1
|
||||
bne 5b
|
||||
|
||||
.endm
|
||||
|
||||
.macro ddr_enter_retention
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, =0x0
|
||||
str r7, [r11, #DDRC_PCTRL_0]
|
||||
|
||||
/* wait rw port_busy clear */
|
||||
ldr r6, =(0x1 << 16)
|
||||
orr r6, r6, #0x1
|
||||
6:
|
||||
ldr r7, [r11, #DDRC_PSTAT]
|
||||
ands r7, r7, r6
|
||||
bne 6b
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
|
||||
/* enter self-refresh bit 5 */
|
||||
ldr r7, =(0x1 << 5)
|
||||
str r7, [r11, #DDRC_PWRCTL]
|
||||
|
||||
/* wait until self-refresh mode entered */
|
||||
7:
|
||||
ldr r7, [r11, #DDRC_STAT]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x3
|
||||
bne 7b
|
||||
8:
|
||||
ldr r7, [r11, #DDRC_STAT]
|
||||
ands r7, r7, #0x20
|
||||
beq 8b
|
||||
|
||||
/* disable dram clk */
|
||||
ldr r7, =(0x1 << 5)
|
||||
orr r7, r7, #(1 << 3)
|
||||
str r7, [r11, #DDRC_PWRCTL]
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
|
||||
ldr r7, [r11, #ANADIG_DIGPROG]
|
||||
and r7, r7, #0xff
|
||||
cmp r7, #0x11
|
||||
bne 10f
|
||||
|
||||
/* TO 1.1 */
|
||||
ldr r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_V_OFFSET]
|
||||
ldr r7, =0x38000000
|
||||
str r7, [r11]
|
||||
|
||||
/* LPSR mode need to use TO1.0 flow as IOMUX lost power */
|
||||
ldr r10, [r0, #PM_INFO_MX7_LPSR_V_OFFSET]
|
||||
ldr r7, [r10]
|
||||
cmp r7, #0x0
|
||||
beq 11f
|
||||
10:
|
||||
/* reset ddr_phy */
|
||||
ldr r11, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
|
||||
ldr r7, =0x0
|
||||
str r7, [r11, #ANADIG_SNVS_MISC_CTRL]
|
||||
|
||||
/* delay 7 us */
|
||||
ldr r6, =6000
|
||||
wait_delay
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
|
||||
ldr r6, =0x1000
|
||||
ldr r7, [r11, r6]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r11, r6]
|
||||
11:
|
||||
/* turn off ddr power */
|
||||
ldr r11, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
|
||||
ldr r7, =(0x1 << 29)
|
||||
str r7, [r11, #ANADIG_SNVS_MISC_CTRL_SET]
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
|
||||
ldr r6, =0x1000
|
||||
ldr r7, [r11, r6]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r11, r6]
|
||||
|
||||
.endm
|
||||
|
||||
.macro ddr_exit_retention
|
||||
|
||||
cmp r5, #0x0
|
||||
ldreq r1, [r0, #PM_INFO_MX7_ANATOP_V_OFFSET]
|
||||
ldrne r1, [r0, #PM_INFO_MX7_ANATOP_P_OFFSET]
|
||||
ldreq r2, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
|
||||
ldrne r2, [r0, #PM_INFO_MX7_SRC_P_OFFSET]
|
||||
ldreq r3, [r0, #PM_INFO_MX7_DDRC_V_OFFSET]
|
||||
ldrne r3, [r0, #PM_INFO_MX7_DDRC_P_OFFSET]
|
||||
ldreq r4, [r0, #PM_INFO_MX7_DDRC_PHY_V_OFFSET]
|
||||
ldrne r4, [r0, #PM_INFO_MX7_DDRC_PHY_P_OFFSET]
|
||||
ldreq r10, [r0, #PM_INFO_MX7_CCM_V_OFFSET]
|
||||
ldrne r10, [r0, #PM_INFO_MX7_CCM_P_OFFSET]
|
||||
ldreq r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_V_OFFSET]
|
||||
ldrne r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_P_OFFSET]
|
||||
|
||||
/* turn on ddr power */
|
||||
ldr r7, =(0x1 << 29)
|
||||
str r7, [r1, #ANADIG_SNVS_MISC_CTRL_CLR]
|
||||
|
||||
ldr r6, =50
|
||||
wait_delay
|
||||
|
||||
/* clear ddr_phy reset */
|
||||
ldr r6, =0x1000
|
||||
ldr r7, [r2, r6]
|
||||
orr r7, r7, #0x3
|
||||
str r7, [r2, r6]
|
||||
ldr r7, [r2, r6]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r2, r6]
|
||||
13:
|
||||
ldr r6, [r0, #PM_INFO_DDRC_REG_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_DDRC_REG_OFFSET
|
||||
add r7, r7, r0
|
||||
14:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r3, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 14b
|
||||
ldr r7, =0x20
|
||||
str r7, [r3, #DDRC_PWRCTL]
|
||||
ldr r7, =0x0
|
||||
str r7, [r3, #DDRC_DFIMISC]
|
||||
|
||||
/* do PHY, clear ddr_phy reset */
|
||||
ldr r6, =0x1000
|
||||
ldr r7, [r2, r6]
|
||||
bic r7, r7, #0x2
|
||||
str r7, [r2, r6]
|
||||
|
||||
ldr r7, [r1, #ANADIG_DIGPROG]
|
||||
and r7, r7, #0xff
|
||||
cmp r7, #0x11
|
||||
bne 12f
|
||||
|
||||
/*
|
||||
* TKT262940:
|
||||
* System hang when press RST for DDR PAD is
|
||||
* in retention mode, fixed on TO1.1
|
||||
*/
|
||||
ldr r7, [r11]
|
||||
bic r7, r7, #(1 << 27)
|
||||
str r7, [r11]
|
||||
ldr r7, [r11]
|
||||
bic r7, r7, #(1 << 29)
|
||||
str r7, [r11]
|
||||
12:
|
||||
ldr r7, =(0x1 << 30)
|
||||
str r7, [r1, #ANADIG_SNVS_MISC_CTRL_SET]
|
||||
|
||||
/* need to delay ~5mS */
|
||||
ldr r6, =0x100000
|
||||
wait_delay
|
||||
|
||||
ldr r6, [r0, #PM_INFO_DDRC_PHY_REG_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_DDRC_PHY_REG_OFFSET
|
||||
add r7, r7, r0
|
||||
|
||||
15:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r4, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 15b
|
||||
|
||||
ldr r7, =0x0
|
||||
add r9, r10, #0x4000
|
||||
str r7, [r9, #0x130]
|
||||
|
||||
ldr r7, =0x170
|
||||
orr r7, r7, #0x8
|
||||
str r7, [r11, #0x20]
|
||||
|
||||
ldr r7, =0x2
|
||||
add r9, r10, #0x4000
|
||||
str r7, [r9, #0x130]
|
||||
|
||||
ldr r7, =0xf
|
||||
str r7, [r4, #DDRPHY_LP_CON0]
|
||||
|
||||
/* wait until self-refresh mode entered */
|
||||
16:
|
||||
ldr r7, [r3, #DDRC_STAT]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x3
|
||||
bne 16b
|
||||
ldr r7, =0x0
|
||||
str r7, [r3, #DDRC_SWCTL]
|
||||
ldr r7, =0x1
|
||||
str r7, [r3, #DDRC_DFIMISC]
|
||||
ldr r7, =0x1
|
||||
str r7, [r3, #DDRC_SWCTL]
|
||||
17:
|
||||
ldr r7, [r3, #DDRC_SWSTAT]
|
||||
and r7, r7, #0x1
|
||||
cmp r7, #0x1
|
||||
bne 17b
|
||||
18:
|
||||
ldr r7, [r3, #DDRC_STAT]
|
||||
and r7, r7, #0x20
|
||||
cmp r7, #0x20
|
||||
bne 18b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, =0x0
|
||||
str r7, [r3, #DDRC_PWRCTL]
|
||||
19:
|
||||
ldr r7, [r3, #DDRC_STAT]
|
||||
and r7, r7, #0x30
|
||||
cmp r7, #0x0
|
||||
bne 19b
|
||||
|
||||
20:
|
||||
ldr r7, [r3, #DDRC_STAT]
|
||||
and r7, r7, #0x3
|
||||
cmp r7, #0x1
|
||||
bne 20b
|
||||
|
||||
/* enable port */
|
||||
ldr r7, =0x1
|
||||
str r7, [r3, #DDRC_PCTRL_0]
|
||||
|
||||
/* enable auto self-refresh */
|
||||
ldr r7, [r3, #DDRC_PWRCTL]
|
||||
orr r7, r7, #(1 << 0)
|
||||
str r7, [r3, #DDRC_PWRCTL]
|
||||
|
||||
.endm
|
||||
|
||||
ENTRY(imx7_suspend)
|
||||
push {r4-r12}
|
||||
|
||||
/* make sure SNVS clk is enabled */
|
||||
ldr r11, [r0, #PM_INFO_MX7_CCM_V_OFFSET]
|
||||
add r11, r11, #0x4000
|
||||
ldr r7, =0x3
|
||||
str r7, [r11, #CCM_SNVS_LPCG]
|
||||
|
||||
/* check whether it is a standby mode */
|
||||
ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
|
||||
ldr r7, [r11, #GPC_PGC_C0]
|
||||
cmp r7, #0
|
||||
beq ddr_only_self_refresh
|
||||
|
||||
/*
|
||||
* The value of r0 is mapped the same in origin table and IRAM table,
|
||||
* thus no need to care r0 here.
|
||||
*/
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
ldr r3, [r0, #PM_INFO_DDR_TYPE_OFFSET]
|
||||
ldr r4, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r6, =imx7_suspend
|
||||
ldr r7, =resume
|
||||
sub r7, r7, r6
|
||||
add r8, r1, r4
|
||||
add r9, r8, r7
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_SRC_V_OFFSET]
|
||||
/* store physical resume addr and pm_info address. */
|
||||
str r9, [r11, #MX7_SRC_GPR1]
|
||||
str r1, [r11, #MX7_SRC_GPR2]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
store_ttbr1
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
|
||||
ldr r7, [r11, #GPC_PGC_FM]
|
||||
cmp r7, #0
|
||||
beq ddr_only_self_refresh
|
||||
|
||||
ddr_enter_retention
|
||||
/* enter LPSR mode if resume addr is valid */
|
||||
ldr r11, [r0, #PM_INFO_MX7_LPSR_V_OFFSET]
|
||||
ldr r7, [r11]
|
||||
cmp r7, #0x0
|
||||
beq ddr_retention_enter_out
|
||||
|
||||
/* disable STOP mode before entering LPSR */
|
||||
ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
|
||||
ldr r7, [r11]
|
||||
bic r7, #0xf
|
||||
str r7, [r11]
|
||||
|
||||
/* shut down vddsoc to enter lpsr mode */
|
||||
ldr r11, [r0, #PM_INFO_MX7_SNVS_V_OFFSET]
|
||||
ldr r7, [r11, #0x38]
|
||||
orr r7, r7, #0x60
|
||||
str r7, [r11, #0x38]
|
||||
wait_shutdown:
|
||||
wfi
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
b wait_shutdown
|
||||
|
||||
ddr_only_self_refresh:
|
||||
ddrc_enter_self_refresh
|
||||
b wfi
|
||||
ddr_retention_enter_out:
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_GIC_DIST_V_OFFSET]
|
||||
ldr r7, =0x0
|
||||
ldr r8, =0x1000
|
||||
str r7, [r11, r8]
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
|
||||
ldr r4, [r11, #MX7D_GPC_IMR1]
|
||||
ldr r5, [r11, #MX7D_GPC_IMR2]
|
||||
ldr r6, [r11, #MX7D_GPC_IMR3]
|
||||
ldr r7, [r11, #MX7D_GPC_IMR4]
|
||||
|
||||
ldr r8, =0xffffffff
|
||||
str r8, [r11, #MX7D_GPC_IMR1]
|
||||
str r8, [r11, #MX7D_GPC_IMR2]
|
||||
str r8, [r11, #MX7D_GPC_IMR3]
|
||||
str r8, [r11, #MX7D_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* enable the RBC bypass counter here
|
||||
* to hold off the interrupts. RBC counter
|
||||
* = 8 (240us). With this setting, the latency
|
||||
* from wakeup interrupt to ARM power up
|
||||
* is ~250uS.
|
||||
*/
|
||||
ldr r8, [r11, #0x14]
|
||||
bic r8, r8, #(0x3f << 24)
|
||||
orr r8, r8, #(0x8 << 24)
|
||||
str r8, [r11, #0x14]
|
||||
|
||||
/* enable the counter. */
|
||||
ldr r8, [r11, #0x14]
|
||||
orr r8, r8, #(0x1 << 30)
|
||||
str r8, [r11, #0x14]
|
||||
|
||||
/* unmask all the GPC interrupts. */
|
||||
str r4, [r11, #MX7D_GPC_IMR1]
|
||||
str r5, [r11, #MX7D_GPC_IMR2]
|
||||
str r6, [r11, #MX7D_GPC_IMR3]
|
||||
str r7, [r11, #MX7D_GPC_IMR4]
|
||||
|
||||
/*
|
||||
* now delay for a short while (3usec)
|
||||
* ARM is at 1GHz at this point
|
||||
* so a short loop should be enough.
|
||||
* this delay is required to ensure that
|
||||
* the RBC counter can start counting in
|
||||
* case an interrupt is already pending
|
||||
* or in case an interrupt arrives just
|
||||
* as ARM is about to assert DSM_request.
|
||||
*/
|
||||
ldr r7, =2000
|
||||
rbc_loop:
|
||||
subs r7, r7, #0x1
|
||||
bne rbc_loop
|
||||
wfi:
|
||||
/* Zzz, enter stop mode */
|
||||
wfi
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
mov r5, #0x0
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
|
||||
ldr r7, [r11, #GPC_PGC_FM]
|
||||
cmp r7, #0
|
||||
beq wfi_ddr_self_refresh_out
|
||||
|
||||
ddr_exit_retention
|
||||
b wfi_ddr_retention_out
|
||||
wfi_ddr_self_refresh_out:
|
||||
ddrc_exit_self_refresh
|
||||
wfi_ddr_retention_out:
|
||||
|
||||
/* check whether it is a standby mode */
|
||||
ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFFSET]
|
||||
ldr r7, [r11, #GPC_PGC_C0]
|
||||
cmp r7, #0
|
||||
beq standby_out
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_GIC_DIST_V_OFFSET]
|
||||
ldr r7, =0x1
|
||||
ldr r8, =0x1000
|
||||
str r7, [r11, r8]
|
||||
|
||||
restore_ttbr1
|
||||
standby_out:
|
||||
pop {r4-r12}
|
||||
/* return to suspend finish */
|
||||
mov pc, lr
|
||||
|
||||
resume:
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r6, #0x0
|
||||
mcr p15, 0, r6, c7, c5, 0
|
||||
mcr p15, 0, r6, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r6, #0x1800
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
/* clear core0's entry and parameter */
|
||||
ldr r11, [r0, #PM_INFO_MX7_SRC_P_OFFSET]
|
||||
mov r7, #0x0
|
||||
str r7, [r11, #MX7_SRC_GPR1]
|
||||
str r7, [r11, #MX7_SRC_GPR2]
|
||||
|
||||
mov r5, #0x1
|
||||
|
||||
ldr r11, [r0, #PM_INFO_MX7_GPC_P_OFFSET]
|
||||
ldr r7, [r11, #GPC_PGC_FM]
|
||||
cmp r7, #0
|
||||
beq dsm_ddr_self_refresh_out
|
||||
|
||||
ddr_exit_retention
|
||||
b dsm_ddr_retention_out
|
||||
dsm_ddr_self_refresh_out:
|
||||
ddrc_exit_self_refresh
|
||||
dsm_ddr_retention_out:
|
||||
|
||||
mov pc, lr
|
||||
ENDPROC(imx7_suspend)
|
||||
|
||||
ENTRY(ca7_cpu_resume)
|
||||
bl v7_invalidate_l1
|
||||
b cpu_resume
|
||||
ENDPROC(ca7_cpu_resume)
|
|
@ -0,0 +1,625 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include "hardware.h"
|
||||
|
||||
/*
|
||||
* ==================== low level suspend ====================
|
||||
*
|
||||
* Better to follow below rules to use ARM registers:
|
||||
* r0: pm_info structure address;
|
||||
*
|
||||
* suspend ocram space layout:
|
||||
* ======================== high address ======================
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* ^
|
||||
* ^
|
||||
* ^
|
||||
* imx7ulp_suspend code
|
||||
* PM_INFO structure(imx7ulp_cpu_pm_info)
|
||||
* ======================== low address =======================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Below offsets are based on struct imx7ulp_cpu_pm_info
|
||||
* which defined in arch/arm/mach-imx/pm-imx7ulp.c, this
|
||||
* structure contains necessary pm info for low level
|
||||
* suspend related code.
|
||||
*/
|
||||
#define PM_INFO_M4_RESERVE0_OFFSET 0x0
|
||||
#define PM_INFO_M4_RESERVE1_OFFSET 0x4
|
||||
#define PM_INFO_M4_RESERVE2_OFFSET 0x8
|
||||
#define PM_INFO_PBASE_OFFSET 0xc
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x10
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x14
|
||||
#define PM_INFO_PM_INFO_SIM_VBASE_OFFSET 0x18
|
||||
#define PM_INFO_PM_INFO_SCG1_VBASE_OFFSET 0x1c
|
||||
#define PM_INFO_PM_INFO_MMDC_VBASE_OFFSET 0x20
|
||||
#define PM_INFO_PM_INFO_MMDC_IO_VBASE_OFFSET 0x24
|
||||
#define PM_INFO_PM_INFO_SMC1_VBASE_OFFSET 0x28
|
||||
#define PM_INFO_PM_INFO_SCG1_VAL_OFFSET 0x2c
|
||||
#define PM_INFO_MX7ULP_TTBR1_V_OFFSET 0x70
|
||||
#define PM_INFO_MX7ULP_GPIO_REG_OFFSET 0x74
|
||||
#define PM_INFO_IOMUX_NUM_OFFSET 0x94
|
||||
#define PM_INFO_IOMUX_VAL_OFFSET 0x98
|
||||
#define PM_INFO_SELECT_INPUT_NUM_OFFSET 0x268
|
||||
#define PM_INFO_SELECT_INPUT_VAL_OFFSET 0x26c
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x3a4
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x3a8
|
||||
/* below offsets depends on MX7ULP_MAX_MMDC_IO_NUM(36) definition */
|
||||
#define PM_INFO_MMDC_NUM_OFFSET 0x5a8
|
||||
#define PM_INFO_MMDC_VAL_OFFSET 0x5ac
|
||||
|
||||
#define DGO_CTRL0 0x50
|
||||
#define DGO_GPR3 0x60
|
||||
#define DGO_GPR4 0x64
|
||||
|
||||
#define MX7ULP_MMDC_MISC 0x18
|
||||
#define MX7ULP_MMDC_MAPSR 0x404
|
||||
#define MX7ULP_MMDC_MPDGCTRL0 0x83c
|
||||
|
||||
#define SCG_RCCR 0x14
|
||||
#define SCG_DDRCCR 0x30
|
||||
#define SCG_NICCCR 0x40
|
||||
#define SCG_FIRCDIV 0x304
|
||||
#define SCG_APLLCSR 0x500
|
||||
#define SCG_APLLDIV 0x504
|
||||
#define SCG_APLLCFG 0x508
|
||||
#define SCG_APLLPFD 0x50c
|
||||
#define SCG_APLLNUM 0x510
|
||||
#define SCG_APLLDENOM 0x514
|
||||
#define SCG_SPLLCSR 0x600
|
||||
#define SCG_SPLLDIV 0x604
|
||||
#define SCG_SPLLCFG 0x608
|
||||
#define SCG_SPLLPFD 0x60c
|
||||
#define SCG_SPLLNUM 0x610
|
||||
#define SCG_SPLLDENOM 0x614
|
||||
#define SCG_SOSCDIV 0x104
|
||||
|
||||
#define PMC1_CTRL 0x24
|
||||
|
||||
#define GPIO_PDOR 0x0
|
||||
#define GPIO_PDDR 0x14
|
||||
#define GPIO_PORT_NUM 0x4
|
||||
#define GPIO_PORT_OFFSET 0x40
|
||||
|
||||
#define PMCTRL 0x10
|
||||
|
||||
#define IOMUX_OFFSET 0x0
|
||||
#define SELECT_INPUT_OFFSET 0x200
|
||||
|
||||
.align 3
|
||||
|
||||
.macro store_ttbr1
|
||||
|
||||
/* Store TTBR1 to pm_info->ttbr1 */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_MX7ULP_TTBR1_V_OFFSET]
|
||||
|
||||
/* 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
|
||||
|
||||
/* Flush the BTAC. */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
ldr r6, =iram_tlb_phys_addr
|
||||
ldr r6, [r6]
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Store the IRAM table in TTBR1 */
|
||||
mcr p15, 0, r6, 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
|
||||
|
||||
.endm
|
||||
|
||||
.macro restore_ttbr1
|
||||
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
orr r6, r6, #0x4
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Restore TTBCR */
|
||||
/* 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
|
||||
|
||||
/* 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
|
||||
|
||||
/* Restore TTBR1, get the origin ttbr1 from pm info */
|
||||
ldr r7, [r0, #PM_INFO_MX7ULP_TTBR1_V_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro restore_mmdc_settings
|
||||
|
||||
ldr r10, =MX7ULP_MMDC_IO_BASE_ADDR
|
||||
ldr r11, =MX7ULP_MMDC_BASE_ADDR
|
||||
|
||||
/* resume mmdc iomuxc settings */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
11:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 11b
|
||||
|
||||
/* restore MMDC settings */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
1:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r11, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 1b
|
||||
|
||||
/* let DDR enter self-refresh */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
2:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq 2b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
3:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
bne 3b
|
||||
|
||||
/* kick off MMDC */
|
||||
ldr r4, =0x0
|
||||
str r4, [r11, #0x1c]
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
4:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
bne 4b
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
.endm
|
||||
|
||||
ENTRY(imx7ulp_suspend)
|
||||
push {r4-r12}
|
||||
|
||||
/*
|
||||
* The value of r0 is mapped the same in origin table and IRAM table,
|
||||
* thus no need to care r0 here.
|
||||
*/
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
ldr r3, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r6, =imx7ulp_suspend
|
||||
ldr r7, =resume
|
||||
sub r7, r7, r6
|
||||
add r8, r1, r3
|
||||
add r9, r8, r7
|
||||
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_SIM_VBASE_OFFSET]
|
||||
/* store physical resume addr and pm_info address. */
|
||||
str r9, [r11, #DGO_GPR3]
|
||||
str r1, [r11, #DGO_GPR4]
|
||||
ldr r7, [r11, #DGO_CTRL0]
|
||||
orr r7, r7, #0xc
|
||||
str r7, [r11, #DGO_CTRL0]
|
||||
wait_dgo:
|
||||
ldr r7, [r11, #DGO_CTRL0]
|
||||
and r7, r7, #0x18000
|
||||
cmp r7, #0x18000
|
||||
bne wait_dgo
|
||||
|
||||
ldr r7, [r11, #DGO_CTRL0]
|
||||
orr r7, r7, #0x18000
|
||||
bic r7, r7, #0xc
|
||||
str r7, [r11, #DGO_CTRL0]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
store_ttbr1
|
||||
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_VBASE_OFFSET]
|
||||
|
||||
/*
|
||||
* put DDR explicitly into self-refresh and
|
||||
* disable automatic power savings.
|
||||
*/
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
/* make the DDR explicitly enter self-refresh. */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
poll_dvfs_set:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq poll_dvfs_set
|
||||
|
||||
/* put mmdc io into lpm */
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_IO_VBASE_OFFSET]
|
||||
ldr r10, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
mmdc_io_lpm:
|
||||
ldr r8, [r7], #0x8
|
||||
mov r9, #0x0
|
||||
str r9, [r11, r8]
|
||||
subs r10, r10, #0x1
|
||||
bne mmdc_io_lpm
|
||||
|
||||
/* switch NIC clock to FIRC */
|
||||
ldr r10, [r0, #PM_INFO_PM_INFO_SCG1_VBASE_OFFSET]
|
||||
ldr r7, [r10, #SCG_NICCCR]
|
||||
bic r7, #(1 << 28)
|
||||
str r7, [r10, #SCG_NICCCR]
|
||||
|
||||
/* switch RUN clock to FIRC */
|
||||
ldr r7, [r10, #SCG_RCCR]
|
||||
bic r7, #(0xf << 24)
|
||||
orr r7, #(0x3 << 24)
|
||||
str r7, [r10, #SCG_RCCR]
|
||||
|
||||
/* turn off SPLL and SPFD */
|
||||
ldr r7, [r10, #SCG_SPLLPFD]
|
||||
mov r8, r7
|
||||
orr r7, r7, #(0x1 << 31)
|
||||
orr r7, r7, #(0x1 << 23)
|
||||
orr r7, r7, #(0x1 << 15)
|
||||
orr r7, r7, #(0x1 << 7)
|
||||
str r7, [r10, #SCG_SPLLPFD]
|
||||
|
||||
ldr r7, [r10, #SCG_SPLLCSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #SCG_SPLLCSR]
|
||||
|
||||
/* turn off APLL and APFD */
|
||||
ldr r7, [r10, #SCG_APLLPFD]
|
||||
mov r9, r7
|
||||
orr r7, r7, #(0x1 << 31)
|
||||
orr r7, r7, #(0x1 << 23)
|
||||
orr r7, r7, #(0x1 << 15)
|
||||
orr r7, r7, #(0x1 << 7)
|
||||
str r7, [r10, #SCG_APLLPFD]
|
||||
|
||||
ldr r7, [r10, #SCG_APLLCSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #SCG_APLLCSR]
|
||||
|
||||
/* Zzz, enter stop mode */
|
||||
wfi
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
/* clear core0's entry and parameter */
|
||||
ldr r10, [r0, #PM_INFO_PM_INFO_SIM_VBASE_OFFSET]
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #DGO_GPR3]
|
||||
str r7, [r10, #DGO_GPR4]
|
||||
|
||||
/* enable SPLL and SPFD */
|
||||
ldr r10, [r0, #PM_INFO_PM_INFO_SCG1_VBASE_OFFSET]
|
||||
ldr r7, [r10, #SCG_SPLLCSR]
|
||||
orr r7, r7, #1
|
||||
str r7, [r10, #SCG_SPLLCSR]
|
||||
wait_spll:
|
||||
ldr r7, [r10, #SCG_SPLLCSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq wait_spll
|
||||
|
||||
str r8, [r10, #SCG_SPLLPFD]
|
||||
/* switch RUN clock to SPLL */
|
||||
ldr r7, [r10, #SCG_RCCR]
|
||||
bic r7, #(0xf << 24)
|
||||
orr r7, #(0x6 << 24)
|
||||
str r7, [r10, #SCG_RCCR]
|
||||
|
||||
/* enable APLL and APFD */
|
||||
ldr r7, [r10, #SCG_APLLCSR]
|
||||
orr r7, r7, #1
|
||||
str r7, [r10, #SCG_APLLCSR]
|
||||
wait_apll:
|
||||
ldr r7, [r10, #SCG_APLLCSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq wait_apll
|
||||
|
||||
str r9, [r10, #SCG_APLLPFD]
|
||||
|
||||
/* switch NIC clock to DDR */
|
||||
ldr r7, [r10, #SCG_NICCCR]
|
||||
orr r7, #(1 << 28)
|
||||
str r7, [r10, #SCG_NICCCR]
|
||||
|
||||
/* let mmdc io out of lpm */
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_IO_VBASE_OFFSET]
|
||||
ldr r10, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
mmdc_io_exit_lpm:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r11, r8]
|
||||
subs r10, r10, #0x1
|
||||
bne mmdc_io_exit_lpm
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_VBASE_OFFSET]
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
poll_dvfs_clear:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
bne poll_dvfs_clear
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
restore_ttbr1
|
||||
pop {r4-r12}
|
||||
/* return to suspend finish */
|
||||
mov pc, lr
|
||||
|
||||
resume:
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r6, #0x0
|
||||
mcr p15, 0, r6, c7, c5, 0
|
||||
mcr p15, 0, r6, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r6, #0x1800
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
isb
|
||||
|
||||
ldr r6, =MX7ULP_SIM_BASE_ADDR
|
||||
ldr r0, [r6, #DGO_GPR4]
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
|
||||
ldr r11, =MX7ULP_SCG1_BASE_ADDR
|
||||
/* enable spll and pfd0 */
|
||||
ldr r5, =PM_INFO_PM_INFO_SCG1_VAL_OFFSET
|
||||
add r6, r5, #48
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLCFG]
|
||||
|
||||
add r6, r5, #56
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLNUM]
|
||||
|
||||
add r6, r5, #60
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLDENOM]
|
||||
|
||||
add r6, r5, #40
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLCSR]
|
||||
5:
|
||||
ldr r7, [r11, #SCG_SPLLCSR]
|
||||
ands r7, r7, #0x1000000
|
||||
beq 5b
|
||||
|
||||
add r6, r5, #44
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLDIV]
|
||||
|
||||
add r6, r5, #52
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLPFD]
|
||||
|
||||
add r6, r5, #0
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_RCCR]
|
||||
|
||||
/* enable apll and pfd0 */
|
||||
add r6, r5, #24
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLCFG]
|
||||
|
||||
add r6, r5, #32
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLNUM]
|
||||
|
||||
add r6, r5, #36
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLDENOM]
|
||||
|
||||
add r6, r5, #16
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLCSR]
|
||||
6:
|
||||
ldr r7, [r11, #SCG_APLLCSR]
|
||||
ands r7, r7, #0x1000000
|
||||
beq 6b
|
||||
|
||||
add r6, r5, #20
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLDIV]
|
||||
|
||||
add r6, r5, #28
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLPFD]
|
||||
|
||||
/* set ddr ccr */
|
||||
add r6, r5, #4
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_DDRCCR]
|
||||
|
||||
/* set nic sel */
|
||||
add r6, r5, #8
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_NICCCR]
|
||||
|
||||
/* set firc div2 to get 48MHz */
|
||||
add r6, r5, #12
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_FIRCDIV]
|
||||
|
||||
/* restore system OSC div */
|
||||
add r6, r5, #64
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SOSCDIV]
|
||||
|
||||
/* enable mmdc clock in pcc3 */
|
||||
ldr r11, =MX7ULP_PCC3_BASE_ADDR
|
||||
ldr r7, [r11, #0xac]
|
||||
orr r7, r7, #(1 << 30)
|
||||
str r7, [r11, #0xac]
|
||||
|
||||
/* enable GPIO clock in pcc2 */
|
||||
ldr r11, =MX7ULP_PCC2_BASE_ADDR
|
||||
ldr r7, [r11, #0x3c]
|
||||
orr r7, r7, #(1 << 30)
|
||||
str r7, [r11, #0x3c]
|
||||
|
||||
/* restore gpio settings */
|
||||
ldr r10, =MX7ULP_GPIOC_BASE_ADDR
|
||||
ldr r7, =PM_INFO_MX7ULP_GPIO_REG_OFFSET
|
||||
add r7, r7, r0
|
||||
ldr r6, =GPIO_PORT_NUM
|
||||
12:
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, #GPIO_PDOR]
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, #GPIO_PDDR]
|
||||
add r10, r10, #GPIO_PORT_OFFSET
|
||||
subs r6, r6, #0x1
|
||||
bne 12b
|
||||
|
||||
/* restore iomuxc settings */
|
||||
ldr r10, =MX7ULP_IOMUXC1_BASE_ADDR
|
||||
add r10, r10, #IOMUX_OFFSET
|
||||
ldr r6, [r0, #PM_INFO_IOMUX_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_IOMUX_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
13:
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10], #0x4
|
||||
subs r6, r6, #0x1
|
||||
bne 13b
|
||||
|
||||
/* restore select input settings */
|
||||
ldr r10, =MX7ULP_IOMUXC1_BASE_ADDR
|
||||
add r10, r10, #SELECT_INPUT_OFFSET
|
||||
ldr r6, [r0, #PM_INFO_SELECT_INPUT_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_SELECT_INPUT_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
14:
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10], #0x4
|
||||
subs r6, r6, #0x1
|
||||
bne 14b
|
||||
|
||||
/* isoack */
|
||||
ldr r6, =MX7ULP_PMC1_BASE_ADDR
|
||||
ldr r7, [r6, #PMC1_CTRL]
|
||||
orr r7, r7, #(1 << 14)
|
||||
str r7, [r6, #PMC1_CTRL]
|
||||
|
||||
restore_mmdc_settings
|
||||
|
||||
mov pc, lr
|
||||
ENDPROC(imx7ulp_suspend)
|
||||
|
||||
ENTRY(imx7ulp_cpu_resume)
|
||||
bl v7_invalidate_l1
|
||||
b cpu_resume
|
||||
ENDPROC(imx7ulp_cpu_resume)
|
|
@ -2320,6 +2320,7 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
|
|||
#endif
|
||||
dev->archdata.dma_ops_setup = true;
|
||||
}
|
||||
EXPORT_SYMBOL(arch_setup_dma_ops);
|
||||
|
||||
void arch_teardown_dma_ops(struct device *dev)
|
||||
{
|
||||
|
|
|
@ -399,6 +399,13 @@ void __iomem *ioremap_wc(resource_size_t res_cookie, size_t size)
|
|||
}
|
||||
EXPORT_SYMBOL(ioremap_wc);
|
||||
|
||||
void __iomem *ioremap_cache_ns(resource_size_t res_cookie, size_t size)
|
||||
{
|
||||
return arch_ioremap_caller(res_cookie, size, MT_MEMORY_RW_NS,
|
||||
__builtin_return_address(0));
|
||||
}
|
||||
EXPORT_SYMBOL(ioremap_cache_ns);
|
||||
|
||||
/*
|
||||
* Remap an arbitrary physical address space into the kernel virtual
|
||||
* address space as memory. Needed when the kernel wants to execute
|
||||
|
|
|
@ -312,6 +312,13 @@ static struct mem_type mem_types[] __ro_after_init = {
|
|||
.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE,
|
||||
.domain = DOMAIN_KERNEL,
|
||||
},
|
||||
[MT_MEMORY_RW_NS] = {
|
||||
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
|
||||
L_PTE_XN,
|
||||
.prot_l1 = PMD_TYPE_TABLE,
|
||||
.prot_sect = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_XN,
|
||||
.domain = DOMAIN_KERNEL,
|
||||
},
|
||||
[MT_ROM] = {
|
||||
.prot_sect = PMD_TYPE_SECT,
|
||||
.domain = DOMAIN_KERNEL,
|
||||
|
@ -648,6 +655,7 @@ static void __init build_mem_type_table(void)
|
|||
}
|
||||
kern_pgprot |= PTE_EXT_AF;
|
||||
vecs_pgprot |= PTE_EXT_AF;
|
||||
mem_types[MT_MEMORY_RW_NS].prot_pte |= PTE_EXT_AF | cp->pte;
|
||||
|
||||
/*
|
||||
* Set PXN for user mappings
|
||||
|
@ -676,6 +684,7 @@ static void __init build_mem_type_table(void)
|
|||
mem_types[MT_MEMORY_RWX].prot_pte |= kern_pgprot;
|
||||
mem_types[MT_MEMORY_RW].prot_sect |= ecc_mask | cp->pmd;
|
||||
mem_types[MT_MEMORY_RW].prot_pte |= kern_pgprot;
|
||||
mem_types[MT_MEMORY_RW_NS].prot_sect |= ecc_mask | cp->pmd;
|
||||
mem_types[MT_MEMORY_DMA_READY].prot_pte |= kern_pgprot;
|
||||
mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |= ecc_mask;
|
||||
mem_types[MT_ROM].prot_sect |= cp->pmd;
|
||||
|
|
|
@ -1046,7 +1046,7 @@ config XEN
|
|||
Say Y if you want to run Linux in a Virtual Machine on Xen on ARM64.
|
||||
|
||||
config FORCE_MAX_ZONEORDER
|
||||
int
|
||||
int "Maximum zone order"
|
||||
default "14" if (ARM64_64K_PAGES && TRANSPARENT_HUGEPAGE)
|
||||
default "12" if (ARM64_16K_PAGES && TRANSPARENT_HUGEPAGE)
|
||||
default "11"
|
||||
|
|
|
@ -171,6 +171,9 @@ config ARCH_MXC
|
|||
select ARM64_ERRATUM_845719 if COMPAT
|
||||
select IMX_GPCV2
|
||||
select IMX_GPCV2_PM_DOMAINS
|
||||
select HAVE_IMX_BUSFREQ
|
||||
select IMX8M_BUSFREQ
|
||||
select IMX8M_PM_DOMAINS
|
||||
select PM
|
||||
select PM_GENERIC_DOMAINS
|
||||
select SOC_BUS
|
||||
|
@ -179,6 +182,9 @@ config ARCH_MXC
|
|||
This enables support for the ARMv8 based SoCs in the
|
||||
NXP i.MX family.
|
||||
|
||||
config HAVE_IMX_BUSFREQ
|
||||
bool "i.MX8M busfreq"
|
||||
|
||||
config ARCH_QCOM
|
||||
bool "Qualcomm Platforms"
|
||||
select GPIOLIB
|
||||
|
|
|
@ -170,6 +170,7 @@ extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size);
|
|||
#define ioremap_nocache(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
|
||||
#define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
|
||||
#define ioremap_wt(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
|
||||
#define ioremap_cache_ns(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NS))
|
||||
|
||||
/*
|
||||
* PCI configuration space mapping function.
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_NC))
|
||||
#define PROT_NORMAL_WT (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL_WT))
|
||||
#define PROT_NORMAL (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
|
||||
#define PROT_NORMAL_NS (PTE_TYPE_PAGE | PTE_AF | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE | PTE_ATTRINDX(MT_NORMAL))
|
||||
|
||||
#define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
|
||||
#define PROT_SECT_NORMAL (PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL))
|
||||
|
@ -77,6 +78,7 @@
|
|||
})
|
||||
|
||||
#define PAGE_S2 __pgprot(_PROT_DEFAULT | PAGE_S2_MEMATTR(NORMAL) | PTE_S2_RDONLY | PAGE_S2_XN)
|
||||
#define PAGE_S2_NS __pgprot(PAGE_S2_MEMATTR(NORMAL) | PTE_S2_RDWR | PTE_TYPE_PAGE | PTE_AF)
|
||||
#define PAGE_S2_DEVICE __pgprot(_PROT_DEFAULT | PAGE_S2_MEMATTR(DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_S2_XN)
|
||||
|
||||
#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN)
|
||||
|
|
|
@ -418,6 +418,11 @@ static inline pmd_t pmd_mkdevmap(pmd_t pmd)
|
|||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
|
||||
#define pgprot_writecombine(prot) \
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
|
||||
#define pgprot_cached(prot) \
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL) | \
|
||||
PTE_PXN | PTE_UXN)
|
||||
#define pgprot_cached_ns(prot) \
|
||||
__pgprot(pgprot_val(pgprot_cached(prot)) ^ PTE_SHARED)
|
||||
#define pgprot_device(prot) \
|
||||
__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
|
||||
/*
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <asm/cputype.h>
|
||||
#include <asm/mmu.h>
|
||||
|
||||
extern bool TKT340553_SW_WORKAROUND;
|
||||
|
||||
/*
|
||||
* Raw TLBI operations.
|
||||
*
|
||||
|
@ -149,8 +151,12 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
|
|||
unsigned long asid = __TLBI_VADDR(0, ASID(mm));
|
||||
|
||||
dsb(ishst);
|
||||
__tlbi(aside1is, asid);
|
||||
__tlbi_user(aside1is, asid);
|
||||
if (TKT340553_SW_WORKAROUND && ASID(mm) >> 12) {
|
||||
__tlbi(vmalle1is);
|
||||
} else {
|
||||
__tlbi(aside1is, asid);
|
||||
__tlbi_user(aside1is, asid);
|
||||
}
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
|
@ -160,8 +166,12 @@ static inline void flush_tlb_page_nosync(struct vm_area_struct *vma,
|
|||
unsigned long addr = __TLBI_VADDR(uaddr, ASID(vma->vm_mm));
|
||||
|
||||
dsb(ishst);
|
||||
__tlbi(vale1is, addr);
|
||||
__tlbi_user(vale1is, addr);
|
||||
if (TKT340553_SW_WORKAROUND && (uaddr >> 36 || (ASID(vma->vm_mm) >> 12))) {
|
||||
__tlbi(vmalle1is);
|
||||
} else {
|
||||
__tlbi(vale1is, addr);
|
||||
__tlbi_user(vale1is, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void flush_tlb_page(struct vm_area_struct *vma,
|
||||
|
@ -183,6 +193,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
|
|||
{
|
||||
unsigned long asid = ASID(vma->vm_mm);
|
||||
unsigned long addr;
|
||||
unsigned long mask = (1 << 20) - 1;
|
||||
|
||||
start = round_down(start, stride);
|
||||
end = round_up(end, stride);
|
||||
|
@ -197,10 +208,13 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
|
|||
|
||||
start = __TLBI_VADDR(start, asid);
|
||||
end = __TLBI_VADDR(end, asid);
|
||||
mask <<= 24;
|
||||
|
||||
dsb(ishst);
|
||||
for (addr = start; addr < end; addr += stride) {
|
||||
if (last_level) {
|
||||
if (TKT340553_SW_WORKAROUND && (addr & mask || (ASID(vma->vm_mm) >> 12))) {
|
||||
__tlbi(vmalle1is);
|
||||
} else if (last_level) {
|
||||
__tlbi(vale1is, addr);
|
||||
__tlbi_user(vale1is, addr);
|
||||
} else {
|
||||
|
@ -234,8 +248,12 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
|
|||
end = __TLBI_VADDR(end, 0);
|
||||
|
||||
dsb(ishst);
|
||||
for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12))
|
||||
__tlbi(vaale1is, addr);
|
||||
for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) {
|
||||
if (TKT340553_SW_WORKAROUND && addr >> 24)
|
||||
__tlbi(vmalle1is);
|
||||
else
|
||||
__tlbi(vaale1is, addr);
|
||||
}
|
||||
dsb(ish);
|
||||
isb();
|
||||
}
|
||||
|
@ -249,7 +267,10 @@ static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr)
|
|||
unsigned long addr = __TLBI_VADDR(kaddr, 0);
|
||||
|
||||
dsb(ishst);
|
||||
__tlbi(vaae1is, addr);
|
||||
if (TKT340553_SW_WORKAROUND && addr >> 24)
|
||||
__tlbi(vmalle1is);
|
||||
else
|
||||
__tlbi(vaae1is, addr);
|
||||
dsb(ish);
|
||||
isb();
|
||||
}
|
||||
|
|
|
@ -57,3 +57,4 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
|
|||
dev->dma_ops = &xen_swiotlb_dma_ops;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(arch_setup_dma_ops);
|
||||
|
|
|
@ -322,7 +322,7 @@ config ARCH_HIBERNATION_POSSIBLE
|
|||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx || \
|
||||
(PPC_85xx && !PPC_E500MC) || PPC_86xx || PPC_PSERIES \
|
||||
FSL_SOC_BOOKE || PPC_86xx || PPC_PSERIES \
|
||||
|| 44x || 40x
|
||||
|
||||
config ARCH_SUSPEND_NONZERO_CPU
|
||||
|
@ -977,8 +977,6 @@ config FSL_PCI
|
|||
|
||||
config FSL_PMC
|
||||
bool
|
||||
default y
|
||||
depends on SUSPEND && (PPC_85xx || PPC_86xx)
|
||||
help
|
||||
Freescale MPC85xx/MPC86xx power management controller support
|
||||
(suspend/resume). For MPC83xx see platforms/83xx/suspend.c
|
||||
|
|
|
@ -42,6 +42,13 @@ extern void flush_dcache_page(struct page *page);
|
|||
#define flush_dcache_mmap_lock(mapping) do { } while (0)
|
||||
#define flush_dcache_mmap_unlock(mapping) do { } while (0)
|
||||
|
||||
extern void __flush_disable_L1(void);
|
||||
#ifdef CONFIG_FSL_SOC_BOOKE
|
||||
extern void flush_dcache_L1(void);
|
||||
#else
|
||||
#define flush_dcache_L1() do { } while (0)
|
||||
#endif
|
||||
|
||||
extern void flush_icache_range(unsigned long, unsigned long);
|
||||
extern void flush_icache_user_range(struct vm_area_struct *vma,
|
||||
struct page *page, unsigned long addr,
|
||||
|
|
|
@ -43,6 +43,14 @@ extern int machine_check_e500mc(struct pt_regs *regs);
|
|||
extern int machine_check_e500(struct pt_regs *regs);
|
||||
extern int machine_check_e200(struct pt_regs *regs);
|
||||
extern int machine_check_47x(struct pt_regs *regs);
|
||||
|
||||
#if defined(CONFIG_E500) || defined(CONFIG_PPC_E500MC)
|
||||
extern void __flush_caches_e500v2(void);
|
||||
extern void __flush_caches_e500mc(void);
|
||||
extern void __flush_caches_e5500(void);
|
||||
extern void __flush_caches_e6500(void);
|
||||
#endif
|
||||
|
||||
int machine_check_8xx(struct pt_regs *regs);
|
||||
int machine_check_83xx(struct pt_regs *regs);
|
||||
|
||||
|
@ -70,6 +78,10 @@ struct cpu_spec {
|
|||
/* flush caches inside the current cpu */
|
||||
void (*cpu_down_flush)(void);
|
||||
|
||||
#if defined(CONFIG_E500) || defined(CONFIG_PPC_E500MC)
|
||||
/* flush caches of the cpu which is running the function */
|
||||
void (*cpu_flush_caches)(void);
|
||||
#endif
|
||||
/* number of performance monitor counters */
|
||||
unsigned int num_pmcs;
|
||||
enum powerpc_pmc_type pmc_type;
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
#ifndef __PPC_FSL_PM_H
|
||||
#define __PPC_FSL_PM_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#define E500_PM_PH10 1
|
||||
#define E500_PM_PH15 2
|
||||
#define E500_PM_PH20 3
|
||||
|
@ -42,6 +45,34 @@ struct fsl_pm_ops {
|
|||
|
||||
extern const struct fsl_pm_ops *qoriq_pm_ops;
|
||||
|
||||
struct fsm_reg_vals {
|
||||
u32 offset;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
void fsl_fsm_setup(void __iomem *base, struct fsm_reg_vals *val);
|
||||
void fsl_epu_setup_default(void __iomem *epu_base);
|
||||
void fsl_npc_setup_default(void __iomem *npc_base);
|
||||
void fsl_fsm_clean(void __iomem *base, struct fsm_reg_vals *val);
|
||||
void fsl_epu_clean_default(void __iomem *epu_base);
|
||||
|
||||
extern int fsl_dp_iomap(void);
|
||||
extern void fsl_dp_iounmap(void);
|
||||
|
||||
extern int fsl_enter_epu_deepsleep(void);
|
||||
extern void fsl_dp_enter_low(void __iomem *ccsr_base, void __iomem *dcsr_base,
|
||||
void __iomem *pld_base, int pld_flag);
|
||||
extern void fsl_booke_deep_sleep_resume(void);
|
||||
|
||||
int __init fsl_rcpm_init(void);
|
||||
|
||||
void set_pm_suspend_state(suspend_state_t state);
|
||||
suspend_state_t pm_suspend_state(void);
|
||||
|
||||
void fsl_set_power_except(struct device *dev, int on);
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#define T1040QDS_TETRA_FLAG 1
|
||||
#define T104xRDB_CPLD_FLAG 2
|
||||
|
||||
#endif /* __PPC_FSL_PM_H */
|
||||
|
|
|
@ -81,6 +81,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
|
|||
ifneq ($(CONFIG_FA_DUMP)$(CONFIG_PRESERVE_FA_DUMP),)
|
||||
obj-y += fadump.o
|
||||
endif
|
||||
obj-$(CONFIG_FSL_SOC) += fsl_pm.o
|
||||
ifdef CONFIG_PPC32
|
||||
obj-$(CONFIG_E500) += idle_e500.o
|
||||
endif
|
||||
|
|
|
@ -365,6 +365,9 @@ int main(void)
|
|||
OFFSET(CPU_SPEC_FEATURES, cpu_spec, cpu_features);
|
||||
OFFSET(CPU_SPEC_SETUP, cpu_spec, cpu_setup);
|
||||
OFFSET(CPU_SPEC_RESTORE, cpu_spec, cpu_restore);
|
||||
#if defined(CONFIG_E500) || defined(CONFIG_PPC_E500MC)
|
||||
OFFSET(CPU_FLUSH_CACHES, cpu_spec, cpu_flush_caches);
|
||||
#endif
|
||||
|
||||
OFFSET(pbe_address, pbe, address);
|
||||
OFFSET(pbe_orig_address, pbe, orig_address);
|
||||
|
|
|
@ -340,3 +340,84 @@ _GLOBAL(cpu_down_flush_e5500)
|
|||
/* L1 Data Cache of e6500 contains no modified data, no flush is required */
|
||||
_GLOBAL(cpu_down_flush_e6500)
|
||||
blr
|
||||
|
||||
_GLOBAL(__flush_caches_e500v2)
|
||||
mflr r0
|
||||
bl flush_dcache_L1
|
||||
mtlr r0
|
||||
blr
|
||||
|
||||
_GLOBAL(__flush_caches_e500mc)
|
||||
_GLOBAL(__flush_caches_e5500)
|
||||
mflr r0
|
||||
bl flush_dcache_L1
|
||||
bl flush_backside_L2_cache
|
||||
mtlr r0
|
||||
blr
|
||||
|
||||
/* L1 Data Cache of e6500 contains no modified data, no flush is required */
|
||||
_GLOBAL(__flush_caches_e6500)
|
||||
blr
|
||||
|
||||
/* r3 = virtual address of L2 controller, WIMG = 01xx */
|
||||
_GLOBAL(flush_disable_L2)
|
||||
/* It's a write-through cache, so only invalidation is needed. */
|
||||
mbar
|
||||
isync
|
||||
lwz r4, 0(r3)
|
||||
li r5, 1
|
||||
rlwimi r4, r5, 30, 0xc0000000
|
||||
stw r4, 0(r3)
|
||||
|
||||
/* Wait for the invalidate to finish */
|
||||
1: lwz r4, 0(r3)
|
||||
andis. r4, r4, 0x4000
|
||||
bne 1b
|
||||
mbar
|
||||
|
||||
blr
|
||||
|
||||
/* r3 = virtual address of L2 controller, WIMG = 01xx */
|
||||
_GLOBAL(invalidate_enable_L2)
|
||||
mbar
|
||||
isync
|
||||
lwz r4, 0(r3)
|
||||
li r5, 3
|
||||
rlwimi r4, r5, 30, 0xc0000000
|
||||
stw r4, 0(r3)
|
||||
|
||||
/* Wait for the invalidate to finish */
|
||||
1: lwz r4, 0(r3)
|
||||
andis. r4, r4, 0x4000
|
||||
bne 1b
|
||||
mbar
|
||||
|
||||
blr
|
||||
|
||||
/* Flush L1 d-cache, invalidate and disable d-cache and i-cache */
|
||||
_GLOBAL(__flush_disable_L1)
|
||||
mflr r10
|
||||
bl flush_dcache_L1 /* Flush L1 d-cache */
|
||||
mtlr r10
|
||||
|
||||
mfspr r4, SPRN_L1CSR0 /* Invalidate and disable d-cache */
|
||||
li r5, 2
|
||||
rlwimi r4, r5, 0, 3
|
||||
|
||||
msync
|
||||
isync
|
||||
mtspr SPRN_L1CSR0, r4
|
||||
isync
|
||||
|
||||
1: mfspr r4, SPRN_L1CSR0 /* Wait for the invalidate to finish */
|
||||
andi. r4, r4, 2
|
||||
bne 1b
|
||||
|
||||
mfspr r4, SPRN_L1CSR1 /* Invalidate and disable i-cache */
|
||||
li r5, 2
|
||||
rlwimi r4, r5, 0, 3
|
||||
|
||||
mtspr SPRN_L1CSR1, r4
|
||||
isync
|
||||
|
||||
blr
|
||||
|
|
|
@ -2051,6 +2051,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
|
|||
.machine_check = machine_check_e500,
|
||||
.platform = "ppc8548",
|
||||
.cpu_down_flush = cpu_down_flush_e500v2,
|
||||
.cpu_flush_caches = __flush_caches_e500v2,
|
||||
},
|
||||
#else
|
||||
{ /* e500mc */
|
||||
|
@ -2071,6 +2072,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
|
|||
.machine_check = machine_check_e500mc,
|
||||
.platform = "ppce500mc",
|
||||
.cpu_down_flush = cpu_down_flush_e500mc,
|
||||
.cpu_flush_caches = __flush_caches_e500mc,
|
||||
},
|
||||
#endif /* CONFIG_PPC_E500MC */
|
||||
#endif /* CONFIG_PPC32 */
|
||||
|
@ -2096,6 +2098,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
|
|||
.machine_check = machine_check_e500mc,
|
||||
.platform = "ppce5500",
|
||||
.cpu_down_flush = cpu_down_flush_e5500,
|
||||
.cpu_flush_caches = __flush_caches_e5500,
|
||||
},
|
||||
{ /* e6500 */
|
||||
.pvr_mask = 0xffff0000,
|
||||
|
@ -2119,6 +2122,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
|
|||
.machine_check = machine_check_e500mc,
|
||||
.platform = "ppce6500",
|
||||
.cpu_down_flush = cpu_down_flush_e6500,
|
||||
.cpu_flush_caches = __flush_caches_e6500,
|
||||
},
|
||||
#endif /* CONFIG_PPC_E500MC */
|
||||
#ifdef CONFIG_PPC32
|
||||
|
|
|
@ -174,6 +174,10 @@ skpinv: addi r6,r6,1 /* Increment */
|
|||
lis r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_NEEDED)@h
|
||||
ori r6,r6,MAS2_VAL(PAGE_OFFSET, BOOK3E_PAGESZ_64M, M_IF_NEEDED)@l
|
||||
mtspr SPRN_MAS2,r6
|
||||
#ifdef ENTRY_DEEPSLEEP_SETUP
|
||||
LOAD_REG_IMMEDIATE(r8, MEMORY_START)
|
||||
ori r8,r8,(MAS3_SX|MAS3_SW|MAS3_SR)
|
||||
#endif
|
||||
mtspr SPRN_MAS3,r8
|
||||
tlbwe
|
||||
|
||||
|
@ -216,12 +220,18 @@ next_tlb_setup:
|
|||
#error You need to specify the mapping or not use this at all.
|
||||
#endif
|
||||
|
||||
#ifdef ENTRY_DEEPSLEEP_SETUP
|
||||
LOAD_REG_ADDR(r6, 2f)
|
||||
mfmsr r7
|
||||
rlwinm r7,r7,0,~(MSR_IS|MSR_DS)
|
||||
#else
|
||||
lis r7,MSR_KERNEL@h
|
||||
ori r7,r7,MSR_KERNEL@l
|
||||
bl 1f /* Find our address */
|
||||
1: mflr r9
|
||||
rlwimi r6,r9,0,20,31
|
||||
addi r6,r6,(2f - 1b)
|
||||
#endif
|
||||
mtspr SPRN_SRR0,r6
|
||||
mtspr SPRN_SRR1,r7
|
||||
rfi /* start execution out of TLB1[0] entry */
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Freescale General Power Management Implementation
|
||||
*
|
||||
* Copyright 2018 NXP
|
||||
* Author: Wang Dongsheng <dongsheng.wang@freescale.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the above-listed copyright holders nor the
|
||||
* names of any contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation, either version 2 of that License or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/fsl_pm.h>
|
||||
|
||||
static suspend_state_t pm_state;
|
||||
|
||||
void set_pm_suspend_state(suspend_state_t state)
|
||||
{
|
||||
pm_state = state;
|
||||
}
|
||||
|
||||
suspend_state_t pm_suspend_state(void)
|
||||
{
|
||||
return pm_state;
|
||||
}
|
|
@ -860,7 +860,7 @@ _GLOBAL(start_secondary_resume)
|
|||
/*
|
||||
* This subroutine clobbers r11 and r12
|
||||
*/
|
||||
enable_64b_mode:
|
||||
_GLOBAL(enable_64b_mode)
|
||||
mfmsr r11 /* grab the current MSR */
|
||||
#ifdef CONFIG_PPC_BOOK3E
|
||||
oris r11,r11,0x8000 /* CM bit set, we'll set ICM later */
|
||||
|
|
|
@ -89,6 +89,12 @@ void print_system_hash_info(void);
|
|||
|
||||
#endif /* CONFIG_PPC_MMU_NOHASH */
|
||||
|
||||
void settlbcam(int index, unsigned long virt, phys_addr_t phys,
|
||||
unsigned long size, unsigned long flags, unsigned int pid);
|
||||
|
||||
void cleartlbcam(unsigned long virt, unsigned int pid);
|
||||
|
||||
|
||||
#ifdef CONFIG_PPC32
|
||||
|
||||
void hash_preload(struct mm_struct *mm, unsigned long ea);
|
||||
|
|
|
@ -102,7 +102,7 @@ unsigned long p_block_mapped(phys_addr_t pa)
|
|||
* an unsigned long (for example, 32-bit implementations cannot support a 4GB
|
||||
* size).
|
||||
*/
|
||||
static void settlbcam(int index, unsigned long virt, phys_addr_t phys,
|
||||
void settlbcam(int index, unsigned long virt, phys_addr_t phys,
|
||||
unsigned long size, unsigned long flags, unsigned int pid)
|
||||
{
|
||||
unsigned int tsize;
|
||||
|
@ -140,6 +140,18 @@ static void settlbcam(int index, unsigned long virt, phys_addr_t phys,
|
|||
tlbcam_addrs[index].phys = phys;
|
||||
}
|
||||
|
||||
void cleartlbcam(unsigned long virt, unsigned int pid)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < NUM_TLBCAMS; i++) {
|
||||
if (tlbcam_addrs[i].start == virt) {
|
||||
TLBCAM[i].MAS1 = 0;
|
||||
loadcam_entry(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long calc_cam_sz(unsigned long ram, unsigned long virt,
|
||||
phys_addr_t phys)
|
||||
{
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
|
||||
#include "mpc83xx.h"
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <asm/io.h>
|
||||
#include <asm/hw_irq.h>
|
||||
#include <asm/ipic.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
|
||||
|
@ -90,24 +89,9 @@ void __init mpc83xx_ipic_init_IRQ(void)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_QUICC_ENGINE
|
||||
void __init mpc83xx_qe_init_IRQ(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
|
||||
if (!np) {
|
||||
np = of_find_node_by_type(NULL, "qeic");
|
||||
if (!np)
|
||||
return;
|
||||
}
|
||||
qe_ic_init(np, 0, qe_ic_cascade_low_ipic, qe_ic_cascade_high_ipic);
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
void __init mpc83xx_ipic_and_qe_init_IRQ(void)
|
||||
{
|
||||
mpc83xx_ipic_init_IRQ();
|
||||
mpc83xx_qe_init_IRQ();
|
||||
}
|
||||
#endif /* CONFIG_QUICC_ENGINE */
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
|
||||
#include "mpc83xx.h"
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include <asm/ipic.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include <sysdev/fsl_pci.h>
|
||||
#include <sysdev/simple_gpio.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
|
||||
#include "mpc83xx.h"
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <asm/ipic.h>
|
||||
#include <asm/udbg.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ menuconfig FSL_SOC_BOOKE
|
|||
select SERIAL_8250_EXTENDED if SERIAL_8250
|
||||
select SERIAL_8250_SHARE_IRQ if SERIAL_8250
|
||||
select FSL_CORENET_RCPM if PPC_E500MC
|
||||
select FSL_QORIQ_PM if SUSPEND && PPC_E500MC
|
||||
select FSL_PMC if SUSPEND && !PPC_E500MC
|
||||
default y
|
||||
|
||||
if FSL_SOC_BOOKE
|
||||
|
@ -292,3 +294,7 @@ endif # FSL_SOC_BOOKE
|
|||
|
||||
config TQM85xx
|
||||
bool
|
||||
|
||||
config FSL_QORIQ_PM
|
||||
bool
|
||||
select FSL_SLEEP_FSM
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
# Makefile for the PowerPC 85xx linux kernel.
|
||||
#
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_SUSPEND) += sleep.o
|
||||
obj-$(CONFIG_FSL_PMC) += mpc85xx_pm_ops.o
|
||||
obj-$(CONFIG_FSL_QORIQ_PM) += qoriq_pm.o deepsleep.o
|
||||
|
||||
obj-y += common.o
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <asm/mpic.h>
|
||||
#include <asm/ehv_pic.h>
|
||||
#include <asm/swiotlb.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
|
||||
#include <linux/of_platform.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
|
@ -38,8 +37,6 @@ void __init corenet_gen_pic_init(void)
|
|||
unsigned int flags = MPIC_BIG_ENDIAN | MPIC_SINGLE_DEST_CPU |
|
||||
MPIC_NO_RESET;
|
||||
|
||||
struct device_node *np;
|
||||
|
||||
if (ppc_md.get_irq == mpic_get_coreint_irq)
|
||||
flags |= MPIC_ENABLE_COREINT;
|
||||
|
||||
|
@ -47,13 +44,6 @@ void __init corenet_gen_pic_init(void)
|
|||
BUG_ON(mpic == NULL);
|
||||
|
||||
mpic_init(mpic);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
|
||||
if (np) {
|
||||
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
|
||||
qe_ic_cascade_high_mpic);
|
||||
of_node_put(np);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* Support deep sleep feature for T104x
|
||||
*
|
||||
* Copyright 2018 NXP
|
||||
* Author: Chenhui Zhao <chenhui.zhao@freescale.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the above-listed copyright holders nor the
|
||||
* names of any contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation, either version 2 of that License or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <asm/machdep.h>
|
||||
#include <asm/fsl_pm.h>
|
||||
|
||||
#define SIZE_1MB 0x100000
|
||||
#define SIZE_2MB 0x200000
|
||||
|
||||
#define CPC_CPCHDBCR0 0x10f00
|
||||
#define CPC_CPCHDBCR0_SPEC_DIS 0x08000000
|
||||
|
||||
#define CCSR_SCFG_DPSLPCR 0xfc000
|
||||
#define CCSR_SCFG_DPSLPCR_WDRR_EN 0x1
|
||||
#define CCSR_SCFG_SPARECR2 0xfc504
|
||||
#define CCSR_SCFG_SPARECR3 0xfc508
|
||||
|
||||
#define CCSR_GPIO1_GPDIR 0x130000
|
||||
#define CCSR_GPIO1_GPODR 0x130004
|
||||
#define CCSR_GPIO1_GPDAT 0x130008
|
||||
#define CCSR_GPIO1_GPDIR_29 0x4
|
||||
|
||||
#define RCPM_BLOCK_OFFSET 0x00022000
|
||||
#define EPU_BLOCK_OFFSET 0x00000000
|
||||
#define NPC_BLOCK_OFFSET 0x00001000
|
||||
|
||||
#define CSTTACR0 0xb00
|
||||
#define CG1CR0 0x31c
|
||||
|
||||
#define CCSR_LAW_BASE 0xC00
|
||||
#define DCFG_BRR 0xE4 /* boot release register */
|
||||
#define LCC_BSTRH 0x20 /* Boot space translation register high */
|
||||
#define LCC_BSTRL 0x24 /* Boot space translation register low */
|
||||
#define LCC_BSTAR 0x28 /* Boot space translation attribute register */
|
||||
#define RCPM_PCTBENR 0x1A0 /* Physical Core Timebase Enable Register */
|
||||
#define RCPM_BASE 0xE2000
|
||||
#define DCFG_BASE 0xE0000
|
||||
|
||||
/* 128 bytes buffer for restoring data broke by DDR training initialization */
|
||||
#define DDR_BUF_SIZE 128
|
||||
static u8 ddr_buff[DDR_BUF_SIZE] __aligned(64);
|
||||
|
||||
static void *dcsr_base, *ccsr_base, *pld_base;
|
||||
static int pld_flag;
|
||||
|
||||
/* for law */
|
||||
struct fsl_law {
|
||||
u32 lawbarh; /* LAWn base address high */
|
||||
u32 lawbarl; /* LAWn base address low */
|
||||
u32 lawar; /* LAWn attributes */
|
||||
u32 reserved;
|
||||
};
|
||||
|
||||
struct fsl_law *saved_law;
|
||||
static u32 num_laws;
|
||||
|
||||
/* for nonboot cpu */
|
||||
struct fsl_bstr {
|
||||
u32 bstrh;
|
||||
u32 bstrl;
|
||||
u32 bstar;
|
||||
u32 cpu_mask;
|
||||
};
|
||||
static struct fsl_bstr saved_bstr;
|
||||
|
||||
int fsl_dp_iomap(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int ret = 0;
|
||||
phys_addr_t ccsr_phy_addr, dcsr_phy_addr;
|
||||
|
||||
saved_law = NULL;
|
||||
ccsr_base = NULL;
|
||||
dcsr_base = NULL;
|
||||
pld_base = NULL;
|
||||
|
||||
ccsr_phy_addr = get_immrbase();
|
||||
if (ccsr_phy_addr == -1) {
|
||||
pr_err("%s: Can't get the address of CCSR\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto ccsr_err;
|
||||
}
|
||||
ccsr_base = ioremap(ccsr_phy_addr, SIZE_2MB);
|
||||
if (!ccsr_base) {
|
||||
ret = -ENOMEM;
|
||||
goto ccsr_err;
|
||||
}
|
||||
|
||||
dcsr_phy_addr = get_dcsrbase();
|
||||
if (dcsr_phy_addr == -1) {
|
||||
pr_err("%s: Can't get the address of DCSR\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto dcsr_err;
|
||||
}
|
||||
dcsr_base = ioremap(dcsr_phy_addr, SIZE_1MB);
|
||||
if (!dcsr_base) {
|
||||
ret = -ENOMEM;
|
||||
goto dcsr_err;
|
||||
}
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,tetra-fpga");
|
||||
if (np) {
|
||||
pld_flag = T1040QDS_TETRA_FLAG;
|
||||
} else {
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,deepsleep-cpld");
|
||||
if (np) {
|
||||
pld_flag = T104xRDB_CPLD_FLAG;
|
||||
} else {
|
||||
pr_err("%s: Can't find the FPGA/CPLD node\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
goto pld_err;
|
||||
}
|
||||
}
|
||||
pld_base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,corenet-law");
|
||||
if (!np) {
|
||||
pr_err("%s: Can't find the node of \"law\"\n", __func__);
|
||||
ret = -EINVAL;
|
||||
goto alloc_err;
|
||||
}
|
||||
ret = of_property_read_u32(np, "fsl,num-laws", &num_laws);
|
||||
if (ret) {
|
||||
ret = -EINVAL;
|
||||
goto alloc_err;
|
||||
}
|
||||
|
||||
saved_law = kzalloc(sizeof(*saved_law) * num_laws, GFP_KERNEL);
|
||||
if (!saved_law) {
|
||||
ret = -ENOMEM;
|
||||
goto alloc_err;
|
||||
}
|
||||
of_node_put(np);
|
||||
|
||||
return 0;
|
||||
|
||||
alloc_err:
|
||||
iounmap(pld_base);
|
||||
pld_base = NULL;
|
||||
pld_err:
|
||||
iounmap(dcsr_base);
|
||||
dcsr_base = NULL;
|
||||
dcsr_err:
|
||||
iounmap(ccsr_base);
|
||||
ccsr_base = NULL;
|
||||
ccsr_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fsl_dp_iounmap(void)
|
||||
{
|
||||
if (dcsr_base) {
|
||||
iounmap(dcsr_base);
|
||||
dcsr_base = NULL;
|
||||
}
|
||||
|
||||
if (ccsr_base) {
|
||||
iounmap(ccsr_base);
|
||||
ccsr_base = NULL;
|
||||
}
|
||||
|
||||
if (pld_base) {
|
||||
iounmap(pld_base);
|
||||
pld_base = NULL;
|
||||
}
|
||||
|
||||
kfree(saved_law);
|
||||
saved_law = NULL;
|
||||
}
|
||||
|
||||
static void fsl_dp_ddr_save(void *ccsr_base)
|
||||
{
|
||||
u32 ddr_buff_addr;
|
||||
|
||||
/*
|
||||
* DDR training initialization will break 128 bytes at the beginning
|
||||
* of DDR, therefore, save them so that the bootloader will restore
|
||||
* them. Assume that DDR is mapped to the address space started with
|
||||
* CONFIG_PAGE_OFFSET.
|
||||
*/
|
||||
memcpy(ddr_buff, (void *)CONFIG_PAGE_OFFSET, DDR_BUF_SIZE);
|
||||
|
||||
/* assume ddr_buff is in the physical address space of 4GB */
|
||||
ddr_buff_addr = (u32)(__pa(ddr_buff) & 0xffffffff);
|
||||
|
||||
/*
|
||||
* the bootloader will restore the first 128 bytes of DDR from
|
||||
* the location indicated by the register SPARECR3
|
||||
*/
|
||||
out_be32(ccsr_base + CCSR_SCFG_SPARECR3, ddr_buff_addr);
|
||||
}
|
||||
|
||||
static void fsl_dp_mp_save(void *ccsr)
|
||||
{
|
||||
struct fsl_bstr *dst = &saved_bstr;
|
||||
|
||||
dst->bstrh = in_be32(ccsr + LCC_BSTRH);
|
||||
dst->bstrl = in_be32(ccsr + LCC_BSTRL);
|
||||
dst->bstar = in_be32(ccsr + LCC_BSTAR);
|
||||
dst->cpu_mask = in_be32(ccsr + DCFG_BASE + DCFG_BRR);
|
||||
}
|
||||
|
||||
static void fsl_dp_mp_restore(void *ccsr)
|
||||
{
|
||||
struct fsl_bstr *src = &saved_bstr;
|
||||
|
||||
out_be32(ccsr + LCC_BSTRH, src->bstrh);
|
||||
out_be32(ccsr + LCC_BSTRL, src->bstrl);
|
||||
out_be32(ccsr + LCC_BSTAR, src->bstar);
|
||||
|
||||
/* release the nonboot cpus */
|
||||
out_be32(ccsr + DCFG_BASE + DCFG_BRR, src->cpu_mask);
|
||||
|
||||
/* enable the time base */
|
||||
out_be32(ccsr + RCPM_BASE + RCPM_PCTBENR, src->cpu_mask);
|
||||
/* read back to sync write */
|
||||
in_be32(ccsr + RCPM_BASE + RCPM_PCTBENR);
|
||||
}
|
||||
|
||||
static void fsl_dp_law_save(void *ccsr)
|
||||
{
|
||||
int i;
|
||||
struct fsl_law *dst = saved_law;
|
||||
struct fsl_law *src = (void *)(ccsr + CCSR_LAW_BASE);
|
||||
|
||||
for (i = 0; i < num_laws; i++) {
|
||||
dst->lawbarh = in_be32(&src->lawbarh);
|
||||
dst->lawbarl = in_be32(&src->lawbarl);
|
||||
dst->lawar = in_be32(&src->lawar);
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_dp_law_restore(void *ccsr)
|
||||
{
|
||||
int i;
|
||||
struct fsl_law *src = saved_law;
|
||||
struct fsl_law *dst = (void *)(ccsr + CCSR_LAW_BASE);
|
||||
|
||||
for (i = 0; i < num_laws - 1; i++) {
|
||||
out_be32(&dst->lawar, 0);
|
||||
out_be32(&dst->lawbarl, src->lawbarl);
|
||||
out_be32(&dst->lawbarh, src->lawbarh);
|
||||
out_be32(&dst->lawar, src->lawar);
|
||||
|
||||
/* Read back so that we sync the writes */
|
||||
in_be32(&dst->lawar);
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void fsl_dp_set_resume_pointer(void *ccsr_base)
|
||||
{
|
||||
u32 resume_addr;
|
||||
|
||||
/* the bootloader will finally jump to this address to return kernel */
|
||||
#ifdef CONFIG_PPC32
|
||||
resume_addr = (u32)(__pa(fsl_booke_deep_sleep_resume));
|
||||
#else
|
||||
resume_addr = (u32)(__pa(*(u64 *)fsl_booke_deep_sleep_resume)
|
||||
& 0xffffffff);
|
||||
#endif
|
||||
|
||||
/* use the register SPARECR2 to save the resume address */
|
||||
out_be32(ccsr_base + CCSR_SCFG_SPARECR2, resume_addr);
|
||||
|
||||
}
|
||||
|
||||
int fsl_enter_epu_deepsleep(void)
|
||||
{
|
||||
fsl_dp_ddr_save(ccsr_base);
|
||||
|
||||
fsl_dp_set_resume_pointer(ccsr_base);
|
||||
|
||||
fsl_dp_mp_save(ccsr_base);
|
||||
fsl_dp_law_save(ccsr_base);
|
||||
/* enable Warm Device Reset request. */
|
||||
setbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
|
||||
|
||||
/* set GPIO1_29 as an output pin (not open-drain), and output 0 */
|
||||
clrbits32(ccsr_base + CCSR_GPIO1_GPDAT, CCSR_GPIO1_GPDIR_29);
|
||||
clrbits32(ccsr_base + CCSR_GPIO1_GPODR, CCSR_GPIO1_GPDIR_29);
|
||||
setbits32(ccsr_base + CCSR_GPIO1_GPDIR, CCSR_GPIO1_GPDIR_29);
|
||||
|
||||
/*
|
||||
* Disable CPC speculation to avoid deep sleep hang, especially
|
||||
* in secure boot mode. This bit will be cleared automatically
|
||||
* when resuming from deep sleep.
|
||||
*/
|
||||
setbits32(ccsr_base + CPC_CPCHDBCR0, CPC_CPCHDBCR0_SPEC_DIS);
|
||||
|
||||
fsl_epu_setup_default(dcsr_base + EPU_BLOCK_OFFSET);
|
||||
fsl_npc_setup_default(dcsr_base + NPC_BLOCK_OFFSET);
|
||||
out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CSTTACR0, 0x00001001);
|
||||
out_be32(dcsr_base + RCPM_BLOCK_OFFSET + CG1CR0, 0x00000001);
|
||||
|
||||
fsl_dp_enter_low(ccsr_base, dcsr_base, pld_base, pld_flag);
|
||||
|
||||
fsl_dp_law_restore(ccsr_base);
|
||||
fsl_dp_mp_restore(ccsr_base);
|
||||
|
||||
/* disable Warm Device Reset request */
|
||||
clrbits32(ccsr_base + CCSR_SCFG_DPSLPCR, CCSR_SCFG_DPSLPCR_WDRR_EN);
|
||||
|
||||
fsl_epu_clean_default(dcsr_base + EPU_BLOCK_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -45,7 +45,6 @@
|
|||
#include <sysdev/fsl_pci.h>
|
||||
#include <sysdev/simple_gpio.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
#include <asm/mpic.h>
|
||||
#include <asm/swiotlb.h>
|
||||
#include "smp.h"
|
||||
|
@ -279,20 +278,6 @@ static void __init mpc85xx_mds_qeic_init(void)
|
|||
of_node_put(np);
|
||||
return;
|
||||
}
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
|
||||
if (!np) {
|
||||
np = of_find_node_by_type(NULL, "qeic");
|
||||
if (!np)
|
||||
return;
|
||||
}
|
||||
|
||||
if (machine_is(p1021_mds))
|
||||
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
|
||||
qe_ic_cascade_high_mpic);
|
||||
else
|
||||
qe_ic_init(np, 0, qe_ic_cascade_muxed_mpic, NULL);
|
||||
of_node_put(np);
|
||||
}
|
||||
#else
|
||||
static void __init mpc85xx_mds_qe_init(void) { }
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <asm/udbg.h>
|
||||
#include <asm/mpic.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
|
@ -44,10 +43,6 @@ void __init mpc85xx_rdb_pic_init(void)
|
|||
{
|
||||
struct mpic *mpic;
|
||||
|
||||
#ifdef CONFIG_QUICC_ENGINE
|
||||
struct device_node *np;
|
||||
#endif
|
||||
|
||||
if (of_machine_is_compatible("fsl,MPC85XXRDB-CAMP")) {
|
||||
mpic = mpic_alloc(NULL, 0, MPIC_NO_RESET |
|
||||
MPIC_BIG_ENDIAN |
|
||||
|
@ -62,18 +57,6 @@ void __init mpc85xx_rdb_pic_init(void)
|
|||
|
||||
BUG_ON(mpic == NULL);
|
||||
mpic_init(mpic);
|
||||
|
||||
#ifdef CONFIG_QUICC_ENGINE
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
|
||||
if (np) {
|
||||
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
|
||||
qe_ic_cascade_high_mpic);
|
||||
of_node_put(np);
|
||||
|
||||
} else
|
||||
pr_err("%s: Could not find qe-ic node\n", __func__);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Support Power Management feature
|
||||
*
|
||||
* Copyright 2018 NXP
|
||||
* Author: Chenhui Zhao <chenhui.zhao@freescale.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the above-listed copyright holders nor the
|
||||
* names of any contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") as published by the Free Software
|
||||
* Foundation, either version 2 of that License or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <asm/fsl_pm.h>
|
||||
|
||||
#define FSL_SLEEP 0x1
|
||||
#define FSL_DEEP_SLEEP 0x2
|
||||
|
||||
int (*fsl_enter_deepsleep)(void);
|
||||
|
||||
/* specify the sleep state of the present platform */
|
||||
unsigned int sleep_pm_state;
|
||||
/* supported sleep modes by the present platform */
|
||||
static unsigned int sleep_modes;
|
||||
|
||||
/**
|
||||
* fsl_set_power_except - set which IP block is not powerdown when sleep,
|
||||
* such as MAC, USB, etc.
|
||||
*
|
||||
* @dev: a pointer to the struct device
|
||||
* @on: if 1, do not power down; if 0, power down.
|
||||
*/
|
||||
void fsl_set_power_except(struct device *dev, int on)
|
||||
{
|
||||
u32 value[2];
|
||||
u32 pw_mask;
|
||||
int ret;
|
||||
struct device_node *mac_node;
|
||||
const phandle *phandle_prop;
|
||||
|
||||
if (dev && !strncmp(dev->bus->name, "usb", 3)) {
|
||||
struct usb_device *udev = container_of(dev,
|
||||
struct usb_device, dev);
|
||||
struct device *controller = udev->bus->controller;
|
||||
|
||||
ret = of_property_read_u32_array(controller->parent->of_node,
|
||||
"sleep", value, 2);
|
||||
} else
|
||||
ret = of_property_read_u32_array(dev->of_node, "sleep",
|
||||
value, 2);
|
||||
|
||||
if (ret) {
|
||||
/* search fman mac node */
|
||||
phandle_prop = of_get_property(dev->of_node, "fsl,fman-mac",
|
||||
NULL);
|
||||
if (phandle_prop == NULL)
|
||||
goto err;
|
||||
|
||||
mac_node = of_find_node_by_phandle(*phandle_prop);
|
||||
ret = of_property_read_u32_array(mac_node, "sleep", value, 2);
|
||||
of_node_put(mac_node);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
/* get the second value, it is a mask */
|
||||
pw_mask = value[1];
|
||||
qoriq_pm_ops->set_ip_power(on, pw_mask);
|
||||
return;
|
||||
|
||||
err:
|
||||
dev_err(dev, "Can not set wakeup sources\n");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_set_power_except);
|
||||
|
||||
void qoriq_set_wakeup_source(struct device *dev, void *enable)
|
||||
{
|
||||
if (!device_may_wakeup(dev))
|
||||
return;
|
||||
|
||||
fsl_set_power_except(dev, *((int *)enable));
|
||||
}
|
||||
|
||||
static int qoriq_suspend_enter(suspend_state_t state)
|
||||
{
|
||||
int ret = 0;
|
||||
int cpu;
|
||||
|
||||
switch (state) {
|
||||
case PM_SUSPEND_STANDBY:
|
||||
|
||||
if (cur_cpu_spec->cpu_flush_caches)
|
||||
cur_cpu_spec->cpu_flush_caches();
|
||||
|
||||
ret = qoriq_pm_ops->plat_enter_sleep();
|
||||
|
||||
break;
|
||||
|
||||
case PM_SUSPEND_MEM:
|
||||
|
||||
cpu = smp_processor_id();
|
||||
qoriq_pm_ops->irq_mask(cpu);
|
||||
|
||||
ret = fsl_enter_deepsleep();
|
||||
|
||||
qoriq_pm_ops->irq_unmask(cpu);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qoriq_suspend_valid(suspend_state_t state)
|
||||
{
|
||||
set_pm_suspend_state(state);
|
||||
|
||||
if (state == PM_SUSPEND_STANDBY && (sleep_modes & FSL_SLEEP))
|
||||
return 1;
|
||||
|
||||
if (state == PM_SUSPEND_MEM && (sleep_modes & FSL_DEEP_SLEEP))
|
||||
return 1;
|
||||
|
||||
set_pm_suspend_state(PM_SUSPEND_ON);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qoriq_suspend_begin(suspend_state_t state)
|
||||
{
|
||||
const int enable = 1;
|
||||
|
||||
dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);
|
||||
|
||||
if (state == PM_SUSPEND_MEM)
|
||||
return fsl_dp_iomap();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qoriq_suspend_end(void)
|
||||
{
|
||||
const int enable = 0;
|
||||
|
||||
dpm_for_each_dev((void *)&enable, qoriq_set_wakeup_source);
|
||||
|
||||
set_pm_suspend_state(PM_SUSPEND_ON);
|
||||
fsl_dp_iounmap();
|
||||
}
|
||||
|
||||
static const struct platform_suspend_ops qoriq_suspend_ops = {
|
||||
.valid = qoriq_suspend_valid,
|
||||
.enter = qoriq_suspend_enter,
|
||||
.begin = qoriq_suspend_begin,
|
||||
.end = qoriq_suspend_end,
|
||||
};
|
||||
|
||||
static const struct of_device_id deepsleep_matches[] = {
|
||||
{
|
||||
.compatible = "fsl,t1040-rcpm",
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,t1024-rcpm",
|
||||
},
|
||||
{
|
||||
.compatible = "fsl,t1023-rcpm",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
static int __init qoriq_suspend_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
sleep_modes = FSL_SLEEP;
|
||||
sleep_pm_state = PLAT_PM_SLEEP;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-rcpm-2.0");
|
||||
if (np)
|
||||
sleep_pm_state = PLAT_PM_LPM20;
|
||||
|
||||
np = of_find_matching_node_and_match(NULL, deepsleep_matches, NULL);
|
||||
if (np) {
|
||||
fsl_enter_deepsleep = fsl_enter_epu_deepsleep;
|
||||
sleep_modes |= FSL_DEEP_SLEEP;
|
||||
}
|
||||
|
||||
suspend_set_ops(&qoriq_suspend_ops);
|
||||
set_pm_suspend_state(PM_SUSPEND_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(qoriq_suspend_init);
|
File diff suppressed because it is too large
Load Diff
|
@ -19,7 +19,6 @@
|
|||
#include <asm/udbg.h>
|
||||
#include <asm/mpic.h>
|
||||
#include <soc/fsl/qe/qe.h>
|
||||
#include <soc/fsl/qe/qe_ic.h>
|
||||
|
||||
#include <sysdev/fsl_soc.h>
|
||||
#include <sysdev/fsl_pci.h>
|
||||
|
@ -31,26 +30,12 @@ static void __init twr_p1025_pic_init(void)
|
|||
{
|
||||
struct mpic *mpic;
|
||||
|
||||
#ifdef CONFIG_QUICC_ENGINE
|
||||
struct device_node *np;
|
||||
#endif
|
||||
|
||||
mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN |
|
||||
MPIC_SINGLE_DEST_CPU,
|
||||
0, 256, " OpenPIC ");
|
||||
|
||||
BUG_ON(mpic == NULL);
|
||||
mpic_init(mpic);
|
||||
|
||||
#ifdef CONFIG_QUICC_ENGINE
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,qe-ic");
|
||||
if (np) {
|
||||
qe_ic_init(np, 0, qe_ic_cascade_low_mpic,
|
||||
qe_ic_cascade_high_mpic);
|
||||
of_node_put(np);
|
||||
} else
|
||||
pr_err("Could not find qe-ic node\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
|
|
|
@ -5,6 +5,7 @@ menuconfig PPC_86xx
|
|||
depends on PPC_BOOK3S_32
|
||||
select FSL_SOC
|
||||
select ALTIVEC
|
||||
select FSL_PMC if SUSPEND
|
||||
help
|
||||
The Freescale E600 SoCs have 74xx cores.
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue