Merge tag 'soc-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc

Pull ARM SoC specific changes from Arnd Bergmann:
 "Lots of changes specific to one of the SoC families.  Some that stick
  out are:

   - mach-qcom gains new features, most importantly SMP support for the
     newer chips (Stephen Boyd, Rohit Vaswani)
   - mvebu gains support for three new SoCs: Armada 375, 380 and 385
     (Thomas Petazzoni and Free-electrons team)
   - SMP support for Rockchips (Heiko Stübner)
   - Lots of i.MX changes (Shawn Guo)
   - Added support for BCM5301x SoC (Hauke Mehrtens)
   - Multiplatform support for Marvell Kirkwood and Dove (Andrew Lunn
     and Sebastian Hesselbarth doing the final part of a long journey)
   - Unify davinci platforms and remove obsolete ones (Sekhar Nori, Arnd
     Bergmann)"

* tag 'soc-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (126 commits)
  ARM: sunxi: Select HAVE_ARM_ARCH_TIMER
  ARM: cache-tauros2: remove ARMv6 code
  ARM: mvebu: don't select CONFIG_NEON
  ARM: davinci: fix DT booting with default defconfig
  ARM: configs: bcm_defconfig: enable bcm590xx regulator support
  ARM: davinci: remove tnetv107x support
  MAINTAINERS: Update ARM STi maintainers
  ARM: restrict BCM_KONA_UART to ARCH_BCM_MOBILE
  ARM: bcm21664: Add board support.
  ARM: sunxi: Add the new watchog compatibles to the reboot code
  ARM: enable ARM_HAS_SG_CHAIN for multiplatform
  ARM: davinci: remove da8xx_omapl_defconfig
  ARM: davinci: da8xx: fix multiple watchdog device registration
  ARM: davinci: add da8xx specific configs to davinci_all_defconfig
  ARM: davinci: enable da8xx build concurrently with older devices
  ARM: BCM5301X: workaround suppress fault
  ARM: BCM5301X: add early debugging support
  ARM: BCM5301X: initial support for the BCM5301X/BCM470X SoCs with ARM CPU
  ARM: mach-bcm: Remove GENERIC_TIME
  ARM: shmobile: APMU: Fix warnings due to improper printk formats
  ...
This commit is contained in:
Linus Torvalds
2014-04-05 14:19:54 -07:00
172 changed files with 3919 additions and 3680 deletions

View File

@@ -1,12 +1,15 @@
config ARCH_MXC
bool "Freescale i.MX family" if ARCH_MULTI_V4_V5 || ARCH_MULTI_V6_V7
select ARCH_HAS_CPUFREQ
select ARCH_HAS_OPP
select ARCH_REQUIRE_GPIOLIB
select ARM_CPU_SUSPEND if PM
select CLKSRC_MMIO
select GENERIC_ALLOCATOR
select GENERIC_IRQ_CHIP
select PINCTRL
select PM_OPP if PM
select SOC_BUS
select SRAM
help
Support for Freescale MXC/iMX-based family of processors
@@ -116,8 +119,8 @@ config SOC_IMX35
select ARCH_MXC_IOMUX_V3
select HAVE_EPIT
select MXC_AVIC
select PINCTRL_IMX35
select SMP_ON_UP if SMP
select PINCTRL
config SOC_IMX5
bool
@@ -768,53 +771,43 @@ config SOC_IMX50
config SOC_IMX53
bool "i.MX53 support"
select HAVE_IMX_SRC
select IMX_HAVE_PLATFORM_IMX2_WDT
select PINCTRL_IMX53
select SOC_IMX5
help
This enables support for Freescale i.MX53 processor.
config SOC_IMX6Q
bool "i.MX6 Quad/DualLite support"
select ARCH_HAS_CPUFREQ
select ARCH_HAS_OPP
config SOC_IMX6
bool
select ARM_ERRATA_754322
select ARM_ERRATA_764369 if SMP
select ARM_ERRATA_775420
select ARM_GIC
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select HAVE_IMX_ANATOP
select HAVE_IMX_GPC
select HAVE_IMX_MMDC
select HAVE_IMX_SRC
select MFD_SYSCON
select MIGHT_HAVE_PCI
select PCI_DOMAINS if PCI
select PINCTRL_IMX6Q
select PL310_ERRATA_588369 if CACHE_PL310
select PL310_ERRATA_727915 if CACHE_PL310
select PL310_ERRATA_769419 if CACHE_PL310
select PM_OPP if PM
config SOC_IMX6Q
bool "i.MX6 Quad/DualLite support"
select ARM_ERRATA_764369 if SMP
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select MIGHT_HAVE_PCI
select PCI_DOMAINS if PCI
select PINCTRL_IMX6Q
select SOC_IMX6
help
This enables support for Freescale i.MX6 Quad processor.
config SOC_IMX6SL
bool "i.MX6 SoloLite support"
select ARM_ERRATA_754322
select ARM_ERRATA_775420
select ARM_GIC
select HAVE_IMX_ANATOP
select HAVE_IMX_GPC
select HAVE_IMX_MMDC
select HAVE_IMX_SRC
select MFD_SYSCON
select PINCTRL_IMX6SL
select PL310_ERRATA_588369 if CACHE_PL310
select PL310_ERRATA_727915 if CACHE_PL310
select PL310_ERRATA_769419 if CACHE_PL310
select SOC_IMX6
help
This enables support for Freescale i.MX6 SoloLite processor.

View File

@@ -30,6 +30,7 @@ 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
endif
ifdef CONFIG_SND_IMX_SOC
@@ -101,9 +102,11 @@ obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o
obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o mach-imx6sl.o
obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
# i.MX6SL reuses i.MX6Q code
obj-$(CONFIG_SOC_IMX6SL) += pm-imx6q.o headsmp.o
ifeq ($(CONFIG_SUSPEND),y)
AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
endif
obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
# i.MX5 based machines
obj-$(CONFIG_MACH_MX51_BABBAGE) += mach-mx51_babbage.o

View File

@@ -149,7 +149,6 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href)
clk_register_clkdev(clk[per1], "per", "imx-gpt.1");
clk_register_clkdev(clk[gpt3_ipg_gate], "ipg", "imx-gpt.2");
clk_register_clkdev(clk[per1], "per", "imx-gpt.2");
clk_register_clkdev(clk[pwm_ipg_gate], "pwm", "mxc_pwm.0");
clk_register_clkdev(clk[per2], "per", "imx21-cspi.0");
clk_register_clkdev(clk[cspi1_ipg_gate], "ipg", "imx21-cspi.0");
clk_register_clkdev(clk[per2], "per", "imx21-cspi.1");

View File

@@ -265,14 +265,6 @@ int __init mx25_clocks_init(void)
clk_register_clkdev(clk[cspi1_ipg], NULL, "imx35-cspi.0");
clk_register_clkdev(clk[cspi2_ipg], NULL, "imx35-cspi.1");
clk_register_clkdev(clk[cspi3_ipg], NULL, "imx35-cspi.2");
clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.0");
clk_register_clkdev(clk[per10], "per", "mxc_pwm.0");
clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.1");
clk_register_clkdev(clk[per10], "per", "mxc_pwm.1");
clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.2");
clk_register_clkdev(clk[per10], "per", "mxc_pwm.2");
clk_register_clkdev(clk[pwm1_ipg], "ipg", "mxc_pwm.3");
clk_register_clkdev(clk[per10], "per", "mxc_pwm.3");
clk_register_clkdev(clk[kpp_ipg], NULL, "imx-keypad");
clk_register_clkdev(clk[tsc_ipg], NULL, "mx25-adc");
clk_register_clkdev(clk[i2c_ipg_per], NULL, "imx21-i2c.0");

View File

@@ -231,7 +231,6 @@ int __init mx27_clocks_init(unsigned long fref)
clk_register_clkdev(clk[per1_gate], "per", "imx-gpt.4");
clk_register_clkdev(clk[gpt6_ipg_gate], "ipg", "imx-gpt.5");
clk_register_clkdev(clk[per1_gate], "per", "imx-gpt.5");
clk_register_clkdev(clk[pwm_ipg_gate], NULL, "mxc_pwm.0");
clk_register_clkdev(clk[per2_gate], "per", "imx21-mmc.0");
clk_register_clkdev(clk[sdhc1_ipg_gate], "ipg", "imx21-mmc.0");
clk_register_clkdev(clk[per2_gate], "per", "imx21-mmc.1");

View File

@@ -266,8 +266,6 @@ static void __init mx5_clocks_common_init(unsigned long rate_ckil,
clk_register_clkdev(clk[IMX5_CLK_ECSPI2_PER_GATE], "per", "imx51-ecspi.1");
clk_register_clkdev(clk[IMX5_CLK_ECSPI2_IPG_GATE], "ipg", "imx51-ecspi.1");
clk_register_clkdev(clk[IMX5_CLK_CSPI_IPG_GATE], NULL, "imx35-cspi.2");
clk_register_clkdev(clk[IMX5_CLK_PWM1_IPG_GATE], "pwm", "mxc_pwm.0");
clk_register_clkdev(clk[IMX5_CLK_PWM2_IPG_GATE], "pwm", "mxc_pwm.1");
clk_register_clkdev(clk[IMX5_CLK_I2C1_GATE], NULL, "imx21-i2c.0");
clk_register_clkdev(clk[IMX5_CLK_I2C2_GATE], NULL, "imx21-i2c.1");
clk_register_clkdev(clk[IMX5_CLK_USBOH3_PER_GATE], "per", "mxc-ehci.0");

View File

@@ -437,12 +437,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk_register_clkdev(clk[gpt_ipg], "ipg", "imx-gpt.0");
clk_register_clkdev(clk[gpt_ipg_per], "per", "imx-gpt.0");
clk_register_clkdev(clk[cko1_sel], "cko1_sel", NULL);
clk_register_clkdev(clk[ahb], "ahb", NULL);
clk_register_clkdev(clk[cko1], "cko1", NULL);
clk_register_clkdev(clk[arm], NULL, "cpu0");
clk_register_clkdev(clk[pll4_post_div], "pll4_post_div", NULL);
clk_register_clkdev(clk[pll4_audio], "pll4_audio", NULL);
clk_register_clkdev(clk[enet_ref], "enet_ref", NULL);
if ((imx_get_soc_revision() != IMX_CHIP_REVISION_1_0) ||
cpu_is_imx6dl()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 Freescale Semiconductor, Inc.
* Copyright 2013-2014 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
@@ -18,27 +18,43 @@
#include "clk.h"
#include "common.h"
static const char const *step_sels[] = { "osc", "pll2_pfd2", };
static const char const *pll1_sw_sels[] = { "pll1_sys", "step", };
static const char const *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", };
static const char const *ocram_sels[] = { "periph", "ocram_alt_sels", };
static const char const *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", };
static const char const *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", };
static const char const *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", };
static const char const *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", };
static const char const *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", };
static const char const *csi_lcdif_sels[] = { "mmdc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", };
static const char const *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", };
static const char const *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", };
static const char const *perclk_sels[] = { "ipg", "osc", };
static const char const *epdc_pxp_sels[] = { "mmdc", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd1", };
static const char const *gpu2d_ovg_sels[] = { "pll3_pfd1", "pll3_usb_otg", "pll2_bus", "pll2_pfd2", };
static const char const *gpu2d_sels[] = { "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", "pll2_bus", };
static const char const *lcdif_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll3_pfd0", "pll3_pfd1", };
static const char const *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", };
static const char const *audio_sels[] = { "pll4_audio_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", };
static const char const *ecspi_sels[] = { "pll3_60m", "osc", };
static const char const *uart_sels[] = { "pll3_80m", "osc", };
#define CCSR 0xc
#define BM_CCSR_PLL1_SW_CLK_SEL (1 << 2)
#define CACRR 0x10
#define CDHIPR 0x48
#define BM_CDHIPR_ARM_PODF_BUSY (1 << 16)
#define ARM_WAIT_DIV_396M 2
#define ARM_WAIT_DIV_792M 4
#define ARM_WAIT_DIV_996M 6
#define PLL_ARM 0x0
#define BM_PLL_ARM_DIV_SELECT (0x7f << 0)
#define BM_PLL_ARM_POWERDOWN (1 << 12)
#define BM_PLL_ARM_ENABLE (1 << 13)
#define BM_PLL_ARM_LOCK (1 << 31)
#define PLL_ARM_DIV_792M 66
static const char *step_sels[] = { "osc", "pll2_pfd2", };
static const char *pll1_sw_sels[] = { "pll1_sys", "step", };
static const char *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", };
static const char *ocram_sels[] = { "periph", "ocram_alt_sels", };
static const char *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", };
static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", };
static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", };
static const char *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", };
static const char *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", };
static const char *csi_lcdif_sels[] = { "mmdc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", };
static const char *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", };
static const char *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", };
static const char *perclk_sels[] = { "ipg", "osc", };
static const char *epdc_pxp_sels[] = { "mmdc", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd1", };
static const char *gpu2d_ovg_sels[] = { "pll3_pfd1", "pll3_usb_otg", "pll2_bus", "pll2_pfd2", };
static const char *gpu2d_sels[] = { "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", "pll2_bus", };
static const char *lcdif_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll3_pfd0", "pll3_pfd1", };
static const char *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", };
static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", };
static const char *ecspi_sels[] = { "pll3_60m", "osc", };
static const char *uart_sels[] = { "pll3_80m", "osc", };
static struct clk_div_table clk_enet_ref_table[] = {
{ .val = 0, .div = 20, },
@@ -65,6 +81,89 @@ static struct clk_div_table video_div_table[] = {
static struct clk *clks[IMX6SL_CLK_END];
static struct clk_onecell_data clk_data;
static void __iomem *ccm_base;
static void __iomem *anatop_base;
static const u32 clks_init_on[] __initconst = {
IMX6SL_CLK_IPG, IMX6SL_CLK_ARM, IMX6SL_CLK_MMDC_ROOT,
};
/*
* ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken
* during WAIT mode entry process could cause cache memory
* corruption.
*
* Software workaround:
* To prevent this issue from occurring, software should ensure that the
* ARM to IPG clock ratio is less than 12:5 (that is < 2.4x), before
* entering WAIT mode.
*
* This function will set the ARM clk to max value within the 12:5 limit.
* As IPG clock is fixed at 66MHz(so ARM freq must not exceed 158.4MHz),
* ARM freq are one of below setpoints: 396MHz, 792MHz and 996MHz, since
* the clk APIs can NOT be called in idle thread(may cause kernel schedule
* as there is sleep function in PLL wait function), so here we just slow
* down ARM to below freq according to previous freq:
*
* run mode wait mode
* 396MHz -> 132MHz;
* 792MHz -> 158.4MHz;
* 996MHz -> 142.3MHz;
*/
static int imx6sl_get_arm_divider_for_wait(void)
{
if (readl_relaxed(ccm_base + CCSR) & BM_CCSR_PLL1_SW_CLK_SEL) {
return ARM_WAIT_DIV_396M;
} else {
if ((readl_relaxed(anatop_base + PLL_ARM) &
BM_PLL_ARM_DIV_SELECT) == PLL_ARM_DIV_792M)
return ARM_WAIT_DIV_792M;
else
return ARM_WAIT_DIV_996M;
}
}
static void imx6sl_enable_pll_arm(bool enable)
{
static u32 saved_pll_arm;
u32 val;
if (enable) {
saved_pll_arm = val = readl_relaxed(anatop_base + PLL_ARM);
val |= BM_PLL_ARM_ENABLE;
val &= ~BM_PLL_ARM_POWERDOWN;
writel_relaxed(val, anatop_base + PLL_ARM);
while (!(__raw_readl(anatop_base + PLL_ARM) & BM_PLL_ARM_LOCK))
;
} else {
writel_relaxed(saved_pll_arm, anatop_base + PLL_ARM);
}
}
void imx6sl_set_wait_clk(bool enter)
{
static unsigned long saved_arm_div;
int arm_div_for_wait = imx6sl_get_arm_divider_for_wait();
/*
* According to hardware design, arm podf change need
* PLL1 clock enabled.
*/
if (arm_div_for_wait == ARM_WAIT_DIV_396M)
imx6sl_enable_pll_arm(true);
if (enter) {
saved_arm_div = readl_relaxed(ccm_base + CACRR);
writel_relaxed(arm_div_for_wait, ccm_base + CACRR);
} else {
writel_relaxed(saved_arm_div, ccm_base + CACRR);
}
while (__raw_readl(ccm_base + CDHIPR) & BM_CDHIPR_ARM_PODF_BUSY)
;
if (arm_div_for_wait == ARM_WAIT_DIV_396M)
imx6sl_enable_pll_arm(false);
}
static void __init imx6sl_clocks_init(struct device_node *ccm_node)
{
@@ -72,6 +171,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
void __iomem *base;
int irq;
int i;
int ret;
clks[IMX6SL_CLK_DUMMY] = imx_clk_fixed("dummy", 0);
clks[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0);
@@ -80,6 +180,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop");
base = of_iomap(np, 0);
WARN_ON(!base);
anatop_base = base;
/* type name parent base div_mask */
clks[IMX6SL_CLK_PLL1_SYS] = imx_clk_pllv3(IMX_PLLV3_SYS, "pll1_sys", "osc", base, 0x7f);
@@ -127,6 +228,7 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
np = ccm_node;
base = of_iomap(np, 0);
WARN_ON(!base);
ccm_base = base;
/* Reuse imx6q pm code */
imx6q_pm_set_ccm_base(base);
@@ -258,6 +360,19 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
clk_register_clkdev(clks[IMX6SL_CLK_GPT], "ipg", "imx-gpt.0");
clk_register_clkdev(clks[IMX6SL_CLK_GPT_SERIAL], "per", "imx-gpt.0");
/* Ensure the AHB clk is at 132MHz. */
ret = clk_set_rate(clks[IMX6SL_CLK_AHB], 132000000);
if (ret)
pr_warn("%s: failed to set AHB clock rate %d!\n",
__func__, ret);
/*
* Make sure those always on clocks are enabled to maintain the correct
* usecount and enabling/disabling of parent PLLs.
*/
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]);
clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]);

View File

@@ -63,25 +63,25 @@ static void __iomem *anatop_base;
static void __iomem *ccm_base;
/* sources for multiplexer clocks, this is used multiple times */
static const char const *fast_sels[] = { "firc", "fxosc", };
static const char const *slow_sels[] = { "sirc_32k", "sxosc", };
static const char const *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
static const char const *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
static const char const *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", };
static const char const *ddr_sels[] = { "pll2_pfd2", "sys_sel", };
static const char const *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", };
static const char const *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", };
static const char const *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
static const char const *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
static const char const *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", };
static const char const *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
static const char const *esdhc_sels[] = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
static const char const *dcu_sels[] = { "pll1_pfd2", "pll3_main", };
static const char const *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", };
static const char const *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", };
static const char *fast_sels[] = { "firc", "fxosc", };
static const char *slow_sels[] = { "sirc_32k", "sxosc", };
static const char *pll1_sels[] = { "pll1_main", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", };
static const char *pll2_sels[] = { "pll2_main", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", };
static const char *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_main", "pll1_pfd_sel", "pll3_main", };
static const char *ddr_sels[] = { "pll2_pfd2", "sys_sel", };
static const char *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", };
static const char *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", };
static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
static const char *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_main_div", };
static const char *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", };
static const char *qspi_sels[] = { "pll3_main", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", };
static const char *esdhc_sels[] = { "pll3_main", "pll3_pfd3", "pll1_pfd3", "platform_bus", };
static const char *dcu_sels[] = { "pll1_pfd2", "pll3_main", };
static const char *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", };
static const char *vadc_sels[] = { "pll6_main_div", "pll3_main_div", "pll3_main", };
/* FTM counter clock source, not module clock */
static const char const *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", };
static const char const *ftm_fix_sels[] = { "sxosc", "ipg_bus", };
static const char *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", };
static const char *ftm_fix_sels[] = { "sxosc", "ipg_bus", };
static struct clk_div_table pll4_main_div_table[] = {
{ .val = 0, .div = 1 },

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2013 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2004-2014 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -116,7 +116,6 @@ void imx_enable_cpu(int cpu, bool enable);
void imx_set_cpu_jump(int cpu, void *jump_addr);
u32 imx_get_cpu_arg(int cpu);
void imx_set_cpu_arg(int cpu, u32 arg);
void v7_cpu_resume(void);
#ifdef CONFIG_SMP
void v7_secondary_startup(void);
void imx_scu_map_io(void);
@@ -139,13 +138,25 @@ void imx_anatop_init(void);
void imx_anatop_pre_suspend(void);
void imx_anatop_post_resume(void);
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode);
void imx6q_set_chicken_bit(void);
void imx6q_set_int_mem_clk_lpm(void);
void imx6sl_set_wait_clk(bool enter);
void imx_cpu_die(unsigned int cpu);
int imx_cpu_kill(unsigned int cpu);
#ifdef CONFIG_SUSPEND
void v7_cpu_resume(void);
void imx6_suspend(void __iomem *ocram_vbase);
#else
static inline void v7_cpu_resume(void) {}
static inline void imx6_suspend(void __iomem *ocram_vbase) {}
#endif
void imx6q_pm_init(void);
void imx6dl_pm_init(void);
void imx6sl_pm_init(void);
void imx6q_pm_set_ccm_base(void __iomem *base);
#ifdef CONFIG_PM
void imx5_pm_init(void);
#else

View File

@@ -68,8 +68,8 @@ int __init imx6q_cpuidle_init(void)
/* Need to enable SCU standby for entering WAIT modes */
imx_scu_standby_enable();
/* Set chicken bit to get a reliable WAIT mode support */
imx6q_set_chicken_bit();
/* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */
imx6q_set_int_mem_clk_lpm();
return cpuidle_register(&imx6q_cpuidle_driver, NULL);
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2014 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/cpuidle.h>
#include <linux/module.h>
#include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include "common.h"
#include "cpuidle.h"
static int imx6sl_enter_wait(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
imx6q_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);
imx6q_set_lpm(WAIT_CLOCKED);
return index;
}
static struct cpuidle_driver imx6sl_cpuidle_driver = {
.name = "imx6sl_cpuidle",
.owner = THIS_MODULE,
.states = {
/* WFI */
ARM_CPUIDLE_WFI_STATE,
/* WAIT */
{
.exit_latency = 50,
.target_residency = 75,
.flags = CPUIDLE_FLAG_TIME_VALID |
CPUIDLE_FLAG_TIMER_STOP,
.enter = imx6sl_enter_wait,
.name = "WAIT",
.desc = "Clock off",
},
},
.state_count = 2,
.safe_state_index = 0,
};
int __init imx6sl_cpuidle_init(void)
{
return cpuidle_register(&imx6sl_cpuidle_driver, NULL);
}

View File

@@ -13,6 +13,7 @@
#ifdef CONFIG_CPU_IDLE
extern int imx5_cpuidle_init(void);
extern int imx6q_cpuidle_init(void);
extern int imx6sl_cpuidle_init(void);
#else
static inline int imx5_cpuidle_init(void)
{
@@ -22,4 +23,8 @@ static inline int imx6q_cpuidle_init(void)
{
return 0;
}
static inline int imx6sl_cpuidle_init(void)
{
return 0;
}
#endif

View File

@@ -83,7 +83,3 @@ extern const struct imx_spi_imx_data imx25_cspi_data[];
#define imx25_add_spi_imx0(pdata) imx25_add_spi_imx(0, pdata)
#define imx25_add_spi_imx1(pdata) imx25_add_spi_imx(1, pdata)
#define imx25_add_spi_imx2(pdata) imx25_add_spi_imx(2, pdata)
extern struct imx_mxc_pwm_data imx25_mxc_pwm_data[];
#define imx25_add_mxc_pwm(id) \
imx_add_mxc_pwm(&imx25_mxc_pwm_data[id])

View File

@@ -57,10 +57,6 @@ extern const struct imx_imx2_wdt_data imx51_imx2_wdt_data[];
#define imx51_add_imx2_wdt(id) \
imx_add_imx2_wdt(&imx51_imx2_wdt_data[id])
extern const struct imx_mxc_pwm_data imx51_mxc_pwm_data[];
#define imx51_add_mxc_pwm(id) \
imx_add_mxc_pwm(&imx51_mxc_pwm_data[id])
extern const struct imx_imx_keypad_data imx51_imx_keypad_data;
#define imx51_add_imx_keypad(pdata) \
imx_add_imx_keypad(&imx51_imx_keypad_data, pdata)

View File

@@ -67,9 +67,6 @@ config IMX_HAVE_PLATFORM_MXC_MMC
config IMX_HAVE_PLATFORM_MXC_NAND
bool
config IMX_HAVE_PLATFORM_MXC_PWM
bool
config IMX_HAVE_PLATFORM_MXC_RNGA
bool
select ARCH_HAS_RNGA

View File

@@ -23,7 +23,6 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MX2_CAMERA) += platform-mx2-camera.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_EHCI) += platform-mxc-ehci.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_MMC) += platform-mxc-mmc.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_NAND) += platform-mxc_nand.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_PWM) += platform-mxc_pwm.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RNGA) += platform-mxc_rnga.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RTC) += platform-mxc_rtc.o
obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o

View File

@@ -290,15 +290,6 @@ struct imx_pata_imx_data {
struct platform_device *__init imx_add_pata_imx(
const struct imx_pata_imx_data *data);
struct imx_mxc_pwm_data {
int id;
resource_size_t iobase;
resource_size_t iosize;
resource_size_t irq;
};
struct platform_device *__init imx_add_mxc_pwm(
const struct imx_mxc_pwm_data *data);
/* mxc_rtc */
struct imx_mxc_rtc_data {
const char *devid;

View File

@@ -1,69 +0,0 @@
/*
* Copyright (C) 2009-2010 Pengutronix
* Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
*
* 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 "../hardware.h"
#include "devices-common.h"
#define imx_mxc_pwm_data_entry_single(soc, _id, _hwid, _size) \
{ \
.id = _id, \
.iobase = soc ## _PWM ## _hwid ## _BASE_ADDR, \
.iosize = _size, \
.irq = soc ## _INT_PWM ## _hwid, \
}
#define imx_mxc_pwm_data_entry(soc, _id, _hwid, _size) \
[_id] = imx_mxc_pwm_data_entry_single(soc, _id, _hwid, _size)
#ifdef CONFIG_SOC_IMX21
const struct imx_mxc_pwm_data imx21_mxc_pwm_data __initconst =
imx_mxc_pwm_data_entry_single(MX21, 0, , SZ_4K);
#endif /* ifdef CONFIG_SOC_IMX21 */
#ifdef CONFIG_SOC_IMX25
const struct imx_mxc_pwm_data imx25_mxc_pwm_data[] __initconst = {
#define imx25_mxc_pwm_data_entry(_id, _hwid) \
imx_mxc_pwm_data_entry(MX25, _id, _hwid, SZ_16K)
imx25_mxc_pwm_data_entry(0, 1),
imx25_mxc_pwm_data_entry(1, 2),
imx25_mxc_pwm_data_entry(2, 3),
imx25_mxc_pwm_data_entry(3, 4),
};
#endif /* ifdef CONFIG_SOC_IMX25 */
#ifdef CONFIG_SOC_IMX27
const struct imx_mxc_pwm_data imx27_mxc_pwm_data __initconst =
imx_mxc_pwm_data_entry_single(MX27, 0, , SZ_4K);
#endif /* ifdef CONFIG_SOC_IMX27 */
#ifdef CONFIG_SOC_IMX51
const struct imx_mxc_pwm_data imx51_mxc_pwm_data[] __initconst = {
#define imx51_mxc_pwm_data_entry(_id, _hwid) \
imx_mxc_pwm_data_entry(MX51, _id, _hwid, SZ_16K)
imx51_mxc_pwm_data_entry(0, 1),
imx51_mxc_pwm_data_entry(1, 2),
};
#endif /* ifdef CONFIG_SOC_IMX51 */
struct platform_device *__init imx_add_mxc_pwm(
const struct imx_mxc_pwm_data *data)
{
struct resource res[] = {
{
.start = data->iobase,
.end = data->iobase + data->iosize - 1,
.flags = IORESOURCE_MEM,
}, {
.start = data->irq,
.end = data->irq,
.flags = IORESOURCE_IRQ,
},
};
return imx_add_platform_device("mxc_pwm", data->id,
res, ARRAY_SIZE(res), NULL, 0);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2004-2007, 2014 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or
@@ -20,7 +20,9 @@
#ifndef __ASM_ARCH_MXC_HARDWARE_H__
#define __ASM_ARCH_MXC_HARDWARE_H__
#ifndef __ASSEMBLY__
#include <asm/io.h>
#endif
#include <asm/sizes.h>
#define addr_in_module(addr, mod) \

View File

@@ -12,12 +12,7 @@
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/asm-offsets.h>
#include <asm/hardware/cache-l2x0.h>
.section ".text.head", "ax"
#ifdef CONFIG_SMP
diag_reg_offset:
.word g_diag_reg - .
@@ -34,38 +29,3 @@ ENTRY(v7_secondary_startup)
set_diag_reg
b secondary_startup
ENDPROC(v7_secondary_startup)
#endif
#ifdef CONFIG_ARM_CPU_SUSPEND
/*
* The following code must assume it is running from physical address
* where absolute virtual addresses to the data section have to be
* turned into relative ones.
*/
#ifdef CONFIG_CACHE_L2X0
.macro pl310_resume
adr r0, l2x0_saved_regs_offset
ldr r2, [r0]
add r2, r2, r0
ldr r0, [r2, #L2X0_R_PHY_BASE] @ get physical base of l2x0
ldr r1, [r2, #L2X0_R_AUX_CTRL] @ get aux_ctrl value
str r1, [r0, #L2X0_AUX_CTRL] @ restore aux_ctrl
mov r1, #0x1
str r1, [r0, #L2X0_CTRL] @ re-enable L2
.endm
l2x0_saved_regs_offset:
.word l2x0_saved_regs - .
#else
.macro pl310_resume
.endm
#endif
ENTRY(v7_cpu_resume)
bl v7_invalidate_l1
pl310_resume
b cpu_resume
ENDPROC(v7_cpu_resume)
#endif

View File

@@ -182,16 +182,83 @@ static void __init imx6q_enet_phy_init(void)
static void __init imx6q_1588_init(void)
{
struct device_node *np;
struct clk *ptp_clk;
struct clk *enet_ref;
struct regmap *gpr;
u32 clksel;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-fec");
if (!np) {
pr_warn("%s: failed to find fec node\n", __func__);
return;
}
ptp_clk = of_clk_get(np, 2);
if (IS_ERR(ptp_clk)) {
pr_warn("%s: failed to get ptp clock\n", __func__);
goto put_node;
}
enet_ref = clk_get_sys(NULL, "enet_ref");
if (IS_ERR(enet_ref)) {
pr_warn("%s: failed to get enet clock\n", __func__);
goto put_ptp_clk;
}
/*
* If enet_ref from ANATOP/CCM is the PTP clock source, we need to
* set bit IOMUXC_GPR1[21]. Or the PTP clock must be from pad
* (external OSC), and we need to clear the bit.
*/
clksel = ptp_clk == enet_ref ? IMX6Q_GPR1_ENET_CLK_SEL_ANATOP :
IMX6Q_GPR1_ENET_CLK_SEL_PAD;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1,
IMX6Q_GPR1_ENET_CLK_SEL_MASK,
IMX6Q_GPR1_ENET_CLK_SEL_ANATOP);
clksel);
else
pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n");
clk_put(enet_ref);
put_ptp_clk:
clk_put(ptp_clk);
put_node:
of_node_put(np);
}
static void __init imx6q_axi_init(void)
{
struct regmap *gpr;
unsigned int mask;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr)) {
/*
* Enable the cacheable attribute of VPU and IPU
* AXI transactions.
*/
mask = IMX6Q_GPR4_VPU_WR_CACHE_SEL |
IMX6Q_GPR4_VPU_RD_CACHE_SEL |
IMX6Q_GPR4_VPU_P_WR_CACHE_VAL |
IMX6Q_GPR4_VPU_P_RD_CACHE_VAL_MASK |
IMX6Q_GPR4_IPU_WR_CACHE_CTL |
IMX6Q_GPR4_IPU_RD_CACHE_CTL;
regmap_update_bits(gpr, IOMUXC_GPR4, mask, mask);
/* Increase IPU read QoS priority */
regmap_update_bits(gpr, IOMUXC_GPR6,
IMX6Q_GPR6_IPU1_ID00_RD_QOS_MASK |
IMX6Q_GPR6_IPU1_ID01_RD_QOS_MASK,
(0xf << 16) | (0x7 << 20));
regmap_update_bits(gpr, IOMUXC_GPR7,
IMX6Q_GPR7_IPU2_ID00_RD_QOS_MASK |
IMX6Q_GPR7_IPU2_ID01_RD_QOS_MASK,
(0xf << 16) | (0x7 << 20));
} else {
pr_warn("failed to find fsl,imx6q-iomuxc-gpr regmap\n");
}
}
static void __init imx6q_init_machine(void)
@@ -212,15 +279,18 @@ static void __init imx6q_init_machine(void)
of_platform_populate(NULL, of_default_bus_match_table, NULL, parent);
imx_anatop_init();
imx6q_pm_init();
cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init();
imx6q_1588_init();
imx6q_axi_init();
}
#define OCOTP_CFG3 0x440
#define OCOTP_CFG3_SPEED_SHIFT 16
#define OCOTP_CFG3_SPEED_1P2GHZ 0x3
#define OCOTP_CFG3_SPEED_996MHZ 0x2
#define OCOTP_CFG3_SPEED_852MHZ 0x1
static void __init imx6q_opp_check_1p2ghz(struct device *cpu_dev)
static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
{
struct device_node *np;
void __iomem *base;
@@ -238,11 +308,29 @@ static void __init imx6q_opp_check_1p2ghz(struct device *cpu_dev)
goto put_node;
}
/*
* SPEED_GRADING[1:0] defines the max speed of ARM:
* 2b'11: 1200000000Hz;
* 2b'10: 996000000Hz;
* 2b'01: 852000000Hz; -- i.MX6Q Only, exclusive with 996MHz.
* 2b'00: 792000000Hz;
* We need to set the max speed of ARM according to fuse map.
*/
val = readl_relaxed(base + OCOTP_CFG3);
val >>= OCOTP_CFG3_SPEED_SHIFT;
if ((val & 0x3) != OCOTP_CFG3_SPEED_1P2GHZ)
val &= 0x3;
if (val != OCOTP_CFG3_SPEED_1P2GHZ)
if (dev_pm_opp_disable(cpu_dev, 1200000000))
pr_warn("failed to disable 1.2 GHz OPP\n");
if (val < OCOTP_CFG3_SPEED_996MHZ)
if (dev_pm_opp_disable(cpu_dev, 996000000))
pr_warn("failed to disable 996 MHz OPP\n");
if (cpu_is_imx6q()) {
if (val != OCOTP_CFG3_SPEED_852MHZ)
if (dev_pm_opp_disable(cpu_dev, 852000000))
pr_warn("failed to disable 852 MHz OPP\n");
}
put_node:
of_node_put(np);
@@ -268,7 +356,7 @@ static void __init imx6q_opp_init(void)
goto put_node;
}
imx6q_opp_check_1p2ghz(cpu_dev);
imx6q_opp_check_speed_grading(cpu_dev);
put_node:
of_node_put(np);

View File

@@ -17,6 +17,7 @@
#include <asm/mach/map.h>
#include "common.h"
#include "cpuidle.h"
static void __init imx6sl_fec_init(void)
{
@@ -39,6 +40,8 @@ static void __init imx6sl_init_late(void)
/* imx6sl reuses imx6q cpufreq driver */
if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
imx6sl_cpuidle_init();
}
static void __init imx6sl_init_machine(void)
@@ -55,8 +58,7 @@ static void __init imx6sl_init_machine(void)
imx6sl_fec_init();
imx_anatop_init();
/* Reuse imx6q pm code */
imx6q_pm_init();
imx6sl_pm_init();
}
static void __init imx6sl_init_irq(void)

551
arch/arm/mach-imx/pm-imx6.c Normal file
View File

@@ -0,0 +1,551 @@
/*
* Copyright 2011-2014 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* 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/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/genalloc.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <asm/cacheflush.h>
#include <asm/fncpy.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h>
#include <asm/tlb.h>
#include "common.h"
#include "hardware.h"
#define CCR 0x0
#define BM_CCR_WB_COUNT (0x7 << 16)
#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
#define BM_CCR_RBC_EN (0x1 << 27)
#define CLPCR 0x54
#define BP_CLPCR_LPM 0
#define BM_CLPCR_LPM (0x3 << 0)
#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
#define BM_CLPCR_SBYOS (0x1 << 6)
#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
#define BM_CLPCR_VSTBY (0x1 << 8)
#define BP_CLPCR_STBY_COUNT 9
#define BM_CLPCR_STBY_COUNT (0x3 << 9)
#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
#define CGPR 0x64
#define BM_CGPR_INT_MEM_CLK_LPM (0x1 << 17)
#define MX6Q_SUSPEND_OCRAM_SIZE 0x1000
#define MX6_MAX_MMDC_IO_NUM 33
static void __iomem *ccm_base;
static void __iomem *suspend_ocram_base;
static void (*imx6_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
/*
* suspend ocram space layout:
* ======================== high address ======================
* .
* .
* .
* ^
* ^
* ^
* imx6_suspend code
* PM_INFO structure(imx6_cpu_pm_info)
* ======================== low address =======================
*/
struct imx6_pm_base {
phys_addr_t pbase;
void __iomem *vbase;
};
struct imx6_pm_socdata {
u32 cpu_type;
const char *mmdc_compat;
const char *src_compat;
const char *iomuxc_compat;
const char *gpc_compat;
const u32 mmdc_io_num;
const u32 *mmdc_io_offset;
};
static const u32 imx6q_mmdc_io_offset[] __initconst = {
0x5ac, 0x5b4, 0x528, 0x520, /* DQM0 ~ DQM3 */
0x514, 0x510, 0x5bc, 0x5c4, /* DQM4 ~ DQM7 */
0x56c, 0x578, 0x588, 0x594, /* CAS, RAS, SDCLK_0, SDCLK_1 */
0x5a8, 0x5b0, 0x524, 0x51c, /* SDQS0 ~ SDQS3 */
0x518, 0x50c, 0x5b8, 0x5c0, /* SDQS4 ~ SDQS7 */
0x784, 0x788, 0x794, 0x79c, /* GPR_B0DS ~ GPR_B3DS */
0x7a0, 0x7a4, 0x7a8, 0x748, /* GPR_B4DS ~ GPR_B7DS */
0x59c, 0x5a0, 0x750, 0x774, /* SODT0, SODT1, MODE_CTL, MODE */
0x74c, /* GPR_ADDS */
};
static const u32 imx6dl_mmdc_io_offset[] __initconst = {
0x470, 0x474, 0x478, 0x47c, /* DQM0 ~ DQM3 */
0x480, 0x484, 0x488, 0x48c, /* DQM4 ~ DQM7 */
0x464, 0x490, 0x4ac, 0x4b0, /* CAS, RAS, SDCLK_0, SDCLK_1 */
0x4bc, 0x4c0, 0x4c4, 0x4c8, /* DRAM_SDQS0 ~ DRAM_SDQS3 */
0x4cc, 0x4d0, 0x4d4, 0x4d8, /* DRAM_SDQS4 ~ DRAM_SDQS7 */
0x764, 0x770, 0x778, 0x77c, /* GPR_B0DS ~ GPR_B3DS */
0x780, 0x784, 0x78c, 0x748, /* GPR_B4DS ~ GPR_B7DS */
0x4b4, 0x4b8, 0x750, 0x760, /* SODT0, SODT1, MODE_CTL, MODE */
0x74c, /* GPR_ADDS */
};
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, SDCKE1, RESET */
};
static const struct imx6_pm_socdata imx6q_pm_data __initconst = {
.cpu_type = MXC_CPU_IMX6Q,
.mmdc_compat = "fsl,imx6q-mmdc",
.src_compat = "fsl,imx6q-src",
.iomuxc_compat = "fsl,imx6q-iomuxc",
.gpc_compat = "fsl,imx6q-gpc",
.mmdc_io_num = ARRAY_SIZE(imx6q_mmdc_io_offset),
.mmdc_io_offset = imx6q_mmdc_io_offset,
};
static const struct imx6_pm_socdata imx6dl_pm_data __initconst = {
.cpu_type = MXC_CPU_IMX6DL,
.mmdc_compat = "fsl,imx6q-mmdc",
.src_compat = "fsl,imx6q-src",
.iomuxc_compat = "fsl,imx6dl-iomuxc",
.gpc_compat = "fsl,imx6q-gpc",
.mmdc_io_num = ARRAY_SIZE(imx6dl_mmdc_io_offset),
.mmdc_io_offset = imx6dl_mmdc_io_offset,
};
static const struct imx6_pm_socdata imx6sl_pm_data __initconst = {
.cpu_type = MXC_CPU_IMX6SL,
.mmdc_compat = "fsl,imx6sl-mmdc",
.src_compat = "fsl,imx6sl-src",
.iomuxc_compat = "fsl,imx6sl-iomuxc",
.gpc_compat = "fsl,imx6sl-gpc",
.mmdc_io_num = ARRAY_SIZE(imx6sl_mmdc_io_offset),
.mmdc_io_offset = imx6sl_mmdc_io_offset,
};
/*
* This structure is for passing necessary data for low level ocram
* suspend code(arch/arm/mach-imx/suspend-imx6.S), if this struct
* definition is changed, the offset definition in
* arch/arm/mach-imx/suspend-imx6.S must be also changed accordingly,
* otherwise, the suspend to ocram function will be broken!
*/
struct imx6_cpu_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 cpu_type;
u32 pm_info_size; /* Size of pm_info. */
struct imx6_pm_base mmdc_base;
struct imx6_pm_base src_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;
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);
void imx6q_set_int_mem_clk_lpm(void)
{
u32 val = readl_relaxed(ccm_base + CGPR);
val |= BM_CGPR_INT_MEM_CLK_LPM;
writel_relaxed(val, ccm_base + CGPR);
}
static void imx6q_enable_rbc(bool enable)
{
u32 val;
/*
* need to mask all interrupts in GPC before
* operating RBC configurations
*/
imx_gpc_mask_all();
/* configure RBC enable bit */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_RBC_EN;
val |= enable ? BM_CCR_RBC_EN : 0;
writel_relaxed(val, ccm_base + CCR);
/* configure RBC count */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_RBC_BYPASS_COUNT;
val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
writel(val, ccm_base + CCR);
/*
* 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_gpc_restore_all();
}
static void imx6q_enable_wb(bool enable)
{
u32 val;
/* configure well bias enable bit */
val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_WB_PER_AT_LPM;
val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
writel_relaxed(val, ccm_base + CLPCR);
/* configure well bias count */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_WB_COUNT;
val |= enable ? BM_CCR_WB_COUNT : 0;
writel_relaxed(val, ccm_base + CCR);
}
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
{
struct irq_data *iomuxc_irq_data = irq_get_irq_data(32);
u32 val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM;
switch (mode) {
case WAIT_CLOCKED:
break;
case WAIT_UNCLOCKED:
val |= 0x1 << BP_CLPCR_LPM;
val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
break;
case STOP_POWER_ON:
val |= 0x2 << BP_CLPCR_LPM;
break;
case WAIT_UNCLOCKED_POWER_OFF:
val |= 0x1 << BP_CLPCR_LPM;
val &= ~BM_CLPCR_VSTBY;
val &= ~BM_CLPCR_SBYOS;
break;
case STOP_POWER_OFF:
val |= 0x2 << BP_CLPCR_LPM;
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
if (cpu_is_imx6sl()) {
val |= BM_CLPCR_BYPASS_PMIC_READY;
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
} else {
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
}
break;
default:
return -EINVAL;
}
/*
* ERR007265: CCM: 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_GINT.
* 2) Software should then unmask IRQ #32 in GPC before setting CCM
* Low-Power mode.
* 3) Software should mask IRQ #32 right after CCM Low-Power mode
* is set (set bits 0-1 of CCM_CLPCR).
*/
imx_gpc_irq_unmask(iomuxc_irq_data);
writel_relaxed(val, ccm_base + CLPCR);
imx_gpc_irq_mask(iomuxc_irq_data);
return 0;
}
static int imx6q_suspend_finish(unsigned long val)
{
if (!imx6_suspend_in_ocram_fn) {
cpu_do_idle();
} else {
/*
* call low level suspend function in ocram,
* as we need to float DDR IO.
*/
local_flush_tlb_all();
imx6_suspend_in_ocram_fn(suspend_ocram_base);
}
return 0;
}
static int imx6q_pm_enter(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_MEM:
imx6q_set_lpm(STOP_POWER_OFF);
imx6q_enable_wb(true);
/*
* For suspend into ocram, asm code already take care of
* RBC setting, so we do NOT need to do that here.
*/
if (!imx6_suspend_in_ocram_fn)
imx6q_enable_rbc(true);
imx_gpc_pre_suspend();
imx_anatop_pre_suspend();
imx_set_cpu_jump(0, v7_cpu_resume);
/* Zzz ... */
cpu_suspend(0, imx6q_suspend_finish);
if (cpu_is_imx6q() || cpu_is_imx6dl())
imx_smp_prepare();
imx_anatop_post_resume();
imx_gpc_post_resume();
imx6q_enable_rbc(false);
imx6q_enable_wb(false);
imx6q_set_lpm(WAIT_CLOCKED);
break;
default:
return -EINVAL;
}
return 0;
}
static const struct platform_suspend_ops imx6q_pm_ops = {
.enter = imx6q_pm_enter,
.valid = suspend_valid_only_mem,
};
void __init imx6q_pm_set_ccm_base(void __iomem *base)
{
ccm_base = base;
}
static int __init imx6_pm_get_base(struct imx6_pm_base *base,
const char *compat)
{
struct device_node *node;
struct resource res;
int ret = 0;
node = of_find_compatible_node(NULL, NULL, compat);
if (!node) {
ret = -ENODEV;
goto out;
}
ret = of_address_to_resource(node, 0, &res);
if (ret)
goto put_node;
base->pbase = res.start;
base->vbase = ioremap(res.start, resource_size(&res));
if (!base->vbase)
ret = -ENOMEM;
put_node:
of_node_put(node);
out:
return ret;
}
static int __init imx6q_suspend_init(const struct imx6_pm_socdata *socdata)
{
phys_addr_t ocram_pbase;
struct device_node *node;
struct platform_device *pdev;
struct imx6_cpu_pm_info *pm_info;
struct gen_pool *ocram_pool;
unsigned long ocram_base;
int i, ret = 0;
const u32 *mmdc_offset_array;
suspend_set_ops(&imx6q_pm_ops);
if (!socdata) {
pr_warn("%s: invalid argument!\n", __func__);
return -EINVAL;
}
node = of_find_compatible_node(NULL, NULL, "mmio-sram");
if (!node) {
pr_warn("%s: failed to find ocram node!\n", __func__);
return -ENODEV;
}
pdev = of_find_device_by_node(node);
if (!pdev) {
pr_warn("%s: failed to find ocram device!\n", __func__);
ret = -ENODEV;
goto put_node;
}
ocram_pool = dev_get_gen_pool(&pdev->dev);
if (!ocram_pool) {
pr_warn("%s: ocram pool unavailable!\n", __func__);
ret = -ENODEV;
goto put_node;
}
ocram_base = gen_pool_alloc(ocram_pool, MX6Q_SUSPEND_OCRAM_SIZE);
if (!ocram_base) {
pr_warn("%s: unable to alloc ocram!\n", __func__);
ret = -ENOMEM;
goto put_node;
}
ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base);
suspend_ocram_base = __arm_ioremap_exec(ocram_pbase,
MX6Q_SUSPEND_OCRAM_SIZE, false);
pm_info = suspend_ocram_base;
pm_info->pbase = ocram_pbase;
pm_info->resume_addr = virt_to_phys(v7_cpu_resume);
pm_info->pm_info_size = sizeof(*pm_info);
/*
* ccm physical address is not used by asm code currently,
* so get ccm virtual address directly, as we already have
* it from ccm driver.
*/
pm_info->ccm_base.vbase = ccm_base;
ret = imx6_pm_get_base(&pm_info->mmdc_base, socdata->mmdc_compat);
if (ret) {
pr_warn("%s: failed to get mmdc base %d!\n", __func__, ret);
goto put_node;
}
ret = imx6_pm_get_base(&pm_info->src_base, socdata->src_compat);
if (ret) {
pr_warn("%s: failed to get src base %d!\n", __func__, ret);
goto src_map_failed;
}
ret = imx6_pm_get_base(&pm_info->iomuxc_base, socdata->iomuxc_compat);
if (ret) {
pr_warn("%s: failed to get iomuxc base %d!\n", __func__, ret);
goto iomuxc_map_failed;
}
ret = imx6_pm_get_base(&pm_info->gpc_base, socdata->gpc_compat);
if (ret) {
pr_warn("%s: failed to get gpc base %d!\n", __func__, ret);
goto gpc_map_failed;
}
ret = imx6_pm_get_base(&pm_info->l2_base, "arm,pl310-cache");
if (ret) {
pr_warn("%s: failed to get pl310-cache base %d!\n",
__func__, ret);
goto pl310_cache_map_failed;
}
pm_info->cpu_type = socdata->cpu_type;
pm_info->mmdc_io_num = socdata->mmdc_io_num;
mmdc_offset_array = socdata->mmdc_io_offset;
for (i = 0; i < pm_info->mmdc_io_num; i++) {
pm_info->mmdc_io_val[i][0] =
mmdc_offset_array[i];
pm_info->mmdc_io_val[i][1] =
readl_relaxed(pm_info->iomuxc_base.vbase +
mmdc_offset_array[i]);
}
imx6_suspend_in_ocram_fn = fncpy(
suspend_ocram_base + sizeof(*pm_info),
&imx6_suspend,
MX6Q_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
goto put_node;
pl310_cache_map_failed:
iounmap(&pm_info->gpc_base.vbase);
gpc_map_failed:
iounmap(&pm_info->iomuxc_base.vbase);
iomuxc_map_failed:
iounmap(&pm_info->src_base.vbase);
src_map_failed:
iounmap(&pm_info->mmdc_base.vbase);
put_node:
of_node_put(node);
return ret;
}
static void __init imx6_pm_common_init(const struct imx6_pm_socdata
*socdata)
{
struct regmap *gpr;
int ret;
WARN_ON(!ccm_base);
if (IS_ENABLED(CONFIG_SUSPEND)) {
ret = imx6q_suspend_init(socdata);
if (ret)
pr_warn("%s: No DDR LPM support with suspend %d!\n",
__func__, ret);
}
/*
* This is for SW workaround step #1 of ERR007265, see comments
* in imx6q_set_lpm for details of this errata.
* Force IOMUXC irq pending, so that the interrupt to GPC can be
* used to deassert dsm_request signal when the signal gets
* asserted unexpectedly.
*/
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
IMX6Q_GPR1_GINT);
}
void __init imx6q_pm_init(void)
{
imx6_pm_common_init(&imx6q_pm_data);
}
void __init imx6dl_pm_init(void)
{
imx6_pm_common_init(&imx6dl_pm_data);
}
void __init imx6sl_pm_init(void)
{
imx6_pm_common_init(&imx6sl_pm_data);
}

View File

@@ -1,240 +0,0 @@
/*
* Copyright 2011-2013 Freescale Semiconductor, Inc.
* Copyright 2011 Linaro Ltd.
*
* 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/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/suspend.h>
#include <asm/cacheflush.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h>
#include <asm/hardware/cache-l2x0.h>
#include "common.h"
#include "hardware.h"
#define CCR 0x0
#define BM_CCR_WB_COUNT (0x7 << 16)
#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
#define BM_CCR_RBC_EN (0x1 << 27)
#define CLPCR 0x54
#define BP_CLPCR_LPM 0
#define BM_CLPCR_LPM (0x3 << 0)
#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
#define BM_CLPCR_SBYOS (0x1 << 6)
#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
#define BM_CLPCR_VSTBY (0x1 << 8)
#define BP_CLPCR_STBY_COUNT 9
#define BM_CLPCR_STBY_COUNT (0x3 << 9)
#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
#define CGPR 0x64
#define BM_CGPR_CHICKEN_BIT (0x1 << 17)
static void __iomem *ccm_base;
void imx6q_set_chicken_bit(void)
{
u32 val = readl_relaxed(ccm_base + CGPR);
val |= BM_CGPR_CHICKEN_BIT;
writel_relaxed(val, ccm_base + CGPR);
}
static void imx6q_enable_rbc(bool enable)
{
u32 val;
/*
* need to mask all interrupts in GPC before
* operating RBC configurations
*/
imx_gpc_mask_all();
/* configure RBC enable bit */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_RBC_EN;
val |= enable ? BM_CCR_RBC_EN : 0;
writel_relaxed(val, ccm_base + CCR);
/* configure RBC count */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_RBC_BYPASS_COUNT;
val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
writel(val, ccm_base + CCR);
/*
* 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_gpc_restore_all();
}
static void imx6q_enable_wb(bool enable)
{
u32 val;
/* configure well bias enable bit */
val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_WB_PER_AT_LPM;
val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
writel_relaxed(val, ccm_base + CLPCR);
/* configure well bias count */
val = readl_relaxed(ccm_base + CCR);
val &= ~BM_CCR_WB_COUNT;
val |= enable ? BM_CCR_WB_COUNT : 0;
writel_relaxed(val, ccm_base + CCR);
}
int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
{
struct irq_data *iomuxc_irq_data = irq_get_irq_data(32);
u32 val = readl_relaxed(ccm_base + CLPCR);
val &= ~BM_CLPCR_LPM;
switch (mode) {
case WAIT_CLOCKED:
break;
case WAIT_UNCLOCKED:
val |= 0x1 << BP_CLPCR_LPM;
val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
break;
case STOP_POWER_ON:
val |= 0x2 << BP_CLPCR_LPM;
break;
case WAIT_UNCLOCKED_POWER_OFF:
val |= 0x1 << BP_CLPCR_LPM;
val &= ~BM_CLPCR_VSTBY;
val &= ~BM_CLPCR_SBYOS;
break;
case STOP_POWER_OFF:
val |= 0x2 << BP_CLPCR_LPM;
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
if (cpu_is_imx6sl()) {
val |= BM_CLPCR_BYPASS_PMIC_READY;
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
} else {
val |= BM_CLPCR_BYP_MMDC_CH1_LPM_HS;
}
break;
default:
return -EINVAL;
}
/*
* ERR007265: CCM: 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_GINT.
* 2) Software should then unmask IRQ #32 in GPC before setting CCM
* Low-Power mode.
* 3) Software should mask IRQ #32 right after CCM Low-Power mode
* is set (set bits 0-1 of CCM_CLPCR).
*/
imx_gpc_irq_unmask(iomuxc_irq_data);
writel_relaxed(val, ccm_base + CLPCR);
imx_gpc_irq_mask(iomuxc_irq_data);
return 0;
}
static int imx6q_suspend_finish(unsigned long val)
{
cpu_do_idle();
return 0;
}
static int imx6q_pm_enter(suspend_state_t state)
{
switch (state) {
case PM_SUSPEND_MEM:
imx6q_set_lpm(STOP_POWER_OFF);
imx6q_enable_wb(true);
imx6q_enable_rbc(true);
imx_gpc_pre_suspend();
imx_anatop_pre_suspend();
imx_set_cpu_jump(0, v7_cpu_resume);
/* Zzz ... */
cpu_suspend(0, imx6q_suspend_finish);
if (cpu_is_imx6q() || cpu_is_imx6dl())
imx_smp_prepare();
imx_anatop_post_resume();
imx_gpc_post_resume();
imx6q_enable_rbc(false);
imx6q_enable_wb(false);
imx6q_set_lpm(WAIT_CLOCKED);
break;
default:
return -EINVAL;
}
return 0;
}
static const struct platform_suspend_ops imx6q_pm_ops = {
.enter = imx6q_pm_enter,
.valid = suspend_valid_only_mem,
};
void __init imx6q_pm_set_ccm_base(void __iomem *base)
{
ccm_base = base;
}
void __init imx6q_pm_init(void)
{
struct regmap *gpr;
WARN_ON(!ccm_base);
/*
* This is for SW workaround step #1 of ERR007265, see comments
* in imx6q_set_lpm for details of this errata.
* Force IOMUXC irq pending, so that the interrupt to GPC can be
* used to deassert dsm_request signal when the signal gets
* asserted unexpectedly.
*/
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr))
regmap_update_bits(gpr, IOMUXC_GPR1, IMX6Q_GPR1_GINT,
IMX6Q_GPR1_GINT);
suspend_set_ops(&imx6q_pm_ops);
}

View File

@@ -0,0 +1,361 @@
/*
* Copyright 2014 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 <asm/hardware/cache-l2x0.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 ======================
* .
* .
* .
* ^
* ^
* ^
* imx6_suspend code
* PM_INFO structure(imx6_cpu_pm_info)
* ======================== low address =======================
*/
/*
* Below offsets are based on struct imx6_cpu_pm_info
* which defined in arch/arm/mach-imx/pm-imx6q.c, this
* structure contains necessary pm info for low level
* suspend related code.
*/
#define PM_INFO_PBASE_OFFSET 0x0
#define PM_INFO_RESUME_ADDR_OFFSET 0x4
#define PM_INFO_CPU_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 MX6Q_SRC_GPR1 0x20
#define MX6Q_SRC_GPR2 0x24
#define MX6Q_MMDC_MAPSR 0x404
#define MX6Q_MMDC_MPDGCTRL0 0x83c
#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
.align 3
.macro sync_l2_cache
/* sync L2 cache to drain L2's buffers to DRAM. */
#ifdef CONFIG_CACHE_L2X0
ldr r11, [r0, #PM_INFO_MX6Q_L2_V_OFFSET]
mov r6, #0x0
str r6, [r11, #L2X0_CACHE_SYNC]
1:
ldr r6, [r11, #L2X0_CACHE_SYNC]
ands r6, r6, #0x1
bne 1b
#endif
.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, #MXC_CPU_IMX6SL
bne 4f
/* reset read FIFO, RST_RD_FIFO */
ldr r7, =MX6Q_MMDC_MPDGCTRL0
ldr r6, [r11, r7]
orr r6, r6, #(1 << 31)
str r6, [r11, r7]
2:
ldr r6, [r11, r7]
ands r6, r6, #(1 << 31)
bne 2b
/* reset FIFO a second time */
ldr r6, [r11, r7]
orr r6, r6, #(1 << 31)
str r6, [r11, r7]
3:
ldr r6, [r11, r7]
ands r6, r6, #(1 << 31)
bne 3b
4:
/* let DDR out of self-refresh */
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
bic r7, r7, #(1 << 21)
str r7, [r11, #MX6Q_MMDC_MAPSR]
5:
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
ands r7, r7, #(1 << 25)
bne 5b
/* enable DDR auto power saving */
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
bic r7, r7, #0x1
str r7, [r11, #MX6Q_MMDC_MAPSR]
.endm
ENTRY(imx6_suspend)
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
ldr r3, [r0, #PM_INFO_CPU_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, =imx6_suspend
ldr r7, =resume
sub r7, r7, r6
add r8, r1, r4
add r9, r8, r7
/*
* make sure TLB contain the addr we want,
* as we will access them after MMDC IO floated.
*/
ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
ldr r6, [r11, #0x0]
ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
ldr r6, [r11, #0x0]
/* use r11 to store the IO address */
ldr r11, [r0, #PM_INFO_MX6Q_SRC_V_OFFSET]
/* store physical resume addr and pm_info address. */
str r9, [r11, #MX6Q_SRC_GPR1]
str r1, [r11, #MX6Q_SRC_GPR2]
/* need to sync L2 cache before DSM. */
sync_l2_cache
ldr r11, [r0, #PM_INFO_MX6Q_MMDC_V_OFFSET]
/*
* put DDR explicitly into self-refresh and
* disable automatic power savings.
*/
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
orr r7, r7, #0x1
str r7, [r11, #MX6Q_MMDC_MAPSR]
/* make the DDR explicitly enter self-refresh. */
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
orr r7, r7, #(1 << 21)
str r7, [r11, #MX6Q_MMDC_MAPSR]
poll_dvfs_set:
ldr r7, [r11, #MX6Q_MMDC_MAPSR]
ands r7, r7, #(1 << 25)
beq poll_dvfs_set
ldr r11, [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
/* i.MX6SL's last 3 IOs need special setting */
cmp r3, #MXC_CPU_IMX6SL
subeq r7, r7, #0x3
set_mmdc_io_lpm:
ldr r9, [r8], #0x8
str r6, [r11, r9]
subs r7, r7, #0x1
bne set_mmdc_io_lpm
cmp r3, #MXC_CPU_IMX6SL
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:
/*
* mask all GPC interrupts before
* enabling the RBC counters to
* avoid the counter starting too
* early if an interupt is already
* pending.
*/
ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
ldr r6, [r11, #MX6Q_GPC_IMR1]
ldr r7, [r11, #MX6Q_GPC_IMR2]
ldr r8, [r11, #MX6Q_GPC_IMR3]
ldr r9, [r11, #MX6Q_GPC_IMR4]
ldr r10, =0xffffffff
str r10, [r11, #MX6Q_GPC_IMR1]
str r10, [r11, #MX6Q_GPC_IMR2]
str r10, [r11, #MX6Q_GPC_IMR3]
str r10, [r11, #MX6Q_GPC_IMR4]
/*
* enable the RBC bypass counter here
* to hold off the interrupts. RBC counter
* = 32 (1ms), Minimum RBC delay should be
* 400us for the analog LDOs to power down.
*/
ldr r11, [r0, #PM_INFO_MX6Q_CCM_V_OFFSET]
ldr r10, [r11, #MX6Q_CCM_CCR]
bic r10, r10, #(0x3f << 21)
orr r10, r10, #(0x20 << 21)
str r10, [r11, #MX6Q_CCM_CCR]
/* enable the counter. */
ldr r10, [r11, #MX6Q_CCM_CCR]
orr r10, r10, #(0x1 << 27)
str r10, [r11, #MX6Q_CCM_CCR]
/* unmask all the GPC interrupts. */
ldr r11, [r0, #PM_INFO_MX6Q_GPC_V_OFFSET]
str r6, [r11, #MX6Q_GPC_IMR1]
str r7, [r11, #MX6Q_GPC_IMR2]
str r8, [r11, #MX6Q_GPC_IMR3]
str r9, [r11, #MX6Q_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 r6, =2000
rbc_loop:
subs r6, r6, #0x1
bne rbc_loop
/* Zzz, enter stop mode */
wfi
nop
nop
nop
nop
/*
* run to here means there is pending
* wakeup source, system should auto
* resume, we need to restore MMDC IO first
*/
mov r5, #0x0
resume_mmdc
/* 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_MX6Q_SRC_P_OFFSET]
mov r7, #0x0
str r7, [r11, #MX6Q_SRC_GPR1]
str r7, [r11, #MX6Q_SRC_GPR2]
ldr r3, [r0, #PM_INFO_CPU_TYPE_OFFSET]
mov r5, #0x1
resume_mmdc
mov pc, lr
ENDPROC(imx6_suspend)
/*
* The following code must assume it is running from physical address
* where absolute virtual addresses to the data section have to be
* turned into relative ones.
*/
#ifdef CONFIG_CACHE_L2X0
.macro pl310_resume
adr r0, l2x0_saved_regs_offset
ldr r2, [r0]
add r2, r2, r0
ldr r0, [r2, #L2X0_R_PHY_BASE] @ get physical base of l2x0
ldr r1, [r2, #L2X0_R_AUX_CTRL] @ get aux_ctrl value
str r1, [r0, #L2X0_AUX_CTRL] @ restore aux_ctrl
mov r1, #0x1
str r1, [r0, #L2X0_CTRL] @ re-enable L2
.endm
l2x0_saved_regs_offset:
.word l2x0_saved_regs - .
#else
.macro pl310_resume
.endm
#endif
ENTRY(v7_cpu_resume)
bl v7_invalidate_l1
pl310_resume
b cpu_resume
ENDPROC(v7_cpu_resume)

View File

@@ -25,6 +25,7 @@
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/sched_clock.h>
@@ -116,11 +117,22 @@ static u64 notrace mxc_read_sched_clock(void)
return sched_clock_reg ? __raw_readl(sched_clock_reg) : 0;
}
static struct delay_timer imx_delay_timer;
static unsigned long imx_read_current_timer(void)
{
return __raw_readl(sched_clock_reg);
}
static int __init mxc_clocksource_init(struct clk *timer_clk)
{
unsigned int c = clk_get_rate(timer_clk);
void __iomem *reg = timer_base + (timer_is_v2() ? V2_TCN : MX1_2_TCN);
imx_delay_timer.read_current_timer = &imx_read_current_timer;
imx_delay_timer.freq = c;
register_current_timer_delay(&imx_delay_timer);
sched_clock_reg = reg;
sched_clock_register(mxc_read_sched_clock, 32, c);