From: Ranjani Vaidyanathan Date: Mon, 26 Aug 2013 18:50:03 +0000 (-0500) Subject: ENGR00277382-2 [MX6SL] Add WaIT mode support for MX6SL. X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=3933e15c61644f1e2513947f27f5ad2c4fe4c26c;p=karo-tx-linux.git ENGR00277382-2 [MX6SL] Add WaIT mode support for MX6SL. Enable WAIT mode support for MX6SL. Need to ensure that the ARM:IPG clock ratio is maintained at 12:5 when WFI is executed. This is the fix for the WAIT mode issue on MX6SL. Set AHB to 132Mhz at boot, which is the recommended freq for AHB. Signed-off-by: Ranjani Vaidyanathan --- diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index e53fb59eaf95..9b80b2328785 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -31,6 +31,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 diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c index cc054d122830..5a7e76af7698 100644 --- a/arch/arm/mach-imx/clk-imx6sl.c +++ b/arch/arm/mach-imx/clk-imx6sl.c @@ -67,6 +67,30 @@ static struct clk_div_table video_div_table[] = { static struct clk *clks[IMX6SL_CLK_CLK_END]; static struct clk_onecell_data clk_data; +static u32 cur_arm_podf; + +/* + * On MX6SL, need to ensure that the ARM:IPG clock ratio is maintained + * within 12:5 when the clocks to ARM are gated when the SOC enters + * WAIT mode. This is necessary to avoid WAIT mode issue (an early + * interrupt waking up the ARM). + * This function will set the ARM clk to max value within the 12:5 limit. + */ +void imx6sl_set_wait_clk(bool enter) +{ + u32 parent_rate = clk_get_rate(clk_get_parent(clks[IMX6SL_CLK_ARM])); + + if (enter) { + u32 ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]); + u32 max_arm_wait_clk = (12 * ipg_rate) / 5; + u32 wait_podf = (parent_rate + max_arm_wait_clk - 1) / + max_arm_wait_clk; + + cur_arm_podf = parent_rate / clk_get_rate(clks[IMX6SL_CLK_ARM]); + clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / wait_podf); + } else + clk_set_rate(clks[IMX6SL_CLK_ARM], parent_rate / cur_arm_podf); +} static void __init imx6sl_clocks_init(struct device_node *ccm_node) { @@ -258,6 +282,11 @@ 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); + /* * To prevent the bus clock from being disabled accidently when * clk_disable() gets called on child clock, let's increment the use diff --git a/arch/arm/mach-imx/common.h b/arch/arm/mach-imx/common.h index 790ca9678843..36647133e3e6 100644 --- a/arch/arm/mach-imx/common.h +++ b/arch/arm/mach-imx/common.h @@ -146,6 +146,7 @@ extern void imx_anatop_post_resume(void); extern void imx_anatop_pu_enable(bool enable); extern int imx6_set_lpm(enum mxc_cpu_pwr_mode mode); extern void imx6_set_cache_lpm_in_wait(bool enable); +extern void imx6sl_set_wait_clk(bool enter); extern void imx_cpu_die(unsigned int cpu); extern int imx_cpu_kill(unsigned int cpu); diff --git a/arch/arm/mach-imx/cpuidle-imx6sl.c b/arch/arm/mach-imx/cpuidle-imx6sl.c new file mode 100644 index 000000000000..7d8ed7c90043 --- /dev/null +++ b/arch/arm/mach-imx/cpuidle-imx6sl.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012-2013 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 +#include +#include +#include +#include + +#include "common.h" +#include "cpuidle.h" + +static int imx6sl_enter_wait(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + imx6_set_lpm(WAIT_UNCLOCKED); + imx6sl_set_wait_clk(true); + cpu_do_idle(); + imx6sl_set_wait_clk(false); + imx6_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); +} diff --git a/arch/arm/mach-imx/cpuidle.h b/arch/arm/mach-imx/cpuidle.h index 786f98ecc145..0cdeeaff08f7 100644 --- a/arch/arm/mach-imx/cpuidle.h +++ b/arch/arm/mach-imx/cpuidle.h @@ -1,5 +1,5 @@ /* - * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012-2013 Freescale Semiconductor, Inc. * Copyright 2012 Linaro Ltd. * * The code contained herein is licensed under the GNU General Public @@ -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 diff --git a/arch/arm/mach-imx/mach-imx6sl.c b/arch/arm/mach-imx/mach-imx6sl.c index 0578373c9bfe..e1c83798bc3d 100644 --- a/arch/arm/mach-imx/mach-imx6sl.c +++ b/arch/arm/mach-imx/mach-imx6sl.c @@ -19,6 +19,7 @@ #include #include "common.h" +#include "cpuidle.h" #include "hardware.h" static struct platform_device imx6sl_cpufreq_pdev = { @@ -94,6 +95,9 @@ static void __init imx6sl_init_late(void) else pr_err("failed to find fsl,imx6sl-iomux-gpr regmap\n"); + /* Init CPUIDLE */ + imx6sl_cpuidle_init(); + if (IS_ENABLED(CONFIG_ARM_IMX6_CPUFREQ)) { imx6sl_opp_init(&imx6sl_cpufreq_pdev.dev); platform_device_register(&imx6sl_cpufreq_pdev);