Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 751f7e99 authored by Anson Huang's avatar Anson Huang Committed by Shawn Guo
Browse files

ARM: imx: add cpuidle support for i.mx6sl



Add cpuidle support for i.MX6SL, currently only support
two cpuidle levels(ARM wfi and WAIT mode), and add software
workaround for WAIT mode errata as below:

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.

Signed-off-by: default avatarAnson Huang <b20788@freescale.com>
Signed-off-by: default avatarShawn Guo <shawn.guo@linaro.org>
parent 848db4a0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+26 −0
Original line number Diff line number Diff line
@@ -66,6 +66,32 @@ static struct clk_div_table video_div_table[] = {
static struct clk *clks[IMX6SL_CLK_END];
static struct clk_onecell_data clk_data;

/*
 * 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.
 */
void imx6sl_set_wait_clk(bool enter)
{
	static unsigned long saved_arm_rate;

	if (enter) {
		unsigned long ipg_rate = clk_get_rate(clks[IMX6SL_CLK_IPG]);
		unsigned long max_arm_wait_rate = (12 * ipg_rate) / 5;
		saved_arm_rate = clk_get_rate(clks[IMX6SL_CLK_ARM]);
		clk_set_rate(clks[IMX6SL_CLK_ARM], max_arm_wait_rate);
	} else {
		clk_set_rate(clks[IMX6SL_CLK_ARM], saved_arm_rate);
	}
}

static void __init imx6sl_clocks_init(struct device_node *ccm_node)
{
	struct device_node *np;
+1 −0
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ 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_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);
+57 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+5 −0
Original line number Diff line number Diff line
@@ -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
Loading