1/*
2 * Copyright (C) 2014 Freescale Semiconductor, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/cpuidle.h>
10#include <linux/cpu_pm.h>
11#include <linux/module.h>
12#include <asm/cpuidle.h>
13#include <asm/suspend.h>
14
15#include "common.h"
16#include "cpuidle.h"
17
18static int imx6sx_idle_finish(unsigned long val)
19{
20	cpu_do_idle();
21
22	return 0;
23}
24
25static int imx6sx_enter_wait(struct cpuidle_device *dev,
26			    struct cpuidle_driver *drv, int index)
27{
28	imx6q_set_lpm(WAIT_UNCLOCKED);
29
30	switch (index) {
31	case 1:
32		cpu_do_idle();
33		break;
34	case 2:
35		imx6_enable_rbc(true);
36		imx_gpc_set_arm_power_in_lpm(true);
37		imx_set_cpu_jump(0, v7_cpu_resume);
38		/* Need to notify there is a cpu pm operation. */
39		cpu_pm_enter();
40		cpu_cluster_pm_enter();
41
42		cpu_suspend(0, imx6sx_idle_finish);
43
44		cpu_cluster_pm_exit();
45		cpu_pm_exit();
46		imx_gpc_set_arm_power_in_lpm(false);
47		imx6_enable_rbc(false);
48		break;
49	default:
50		break;
51	}
52
53	imx6q_set_lpm(WAIT_CLOCKED);
54
55	return index;
56}
57
58static struct cpuidle_driver imx6sx_cpuidle_driver = {
59	.name = "imx6sx_cpuidle",
60	.owner = THIS_MODULE,
61	.states = {
62		/* WFI */
63		ARM_CPUIDLE_WFI_STATE,
64		/* WAIT */
65		{
66			.exit_latency = 50,
67			.target_residency = 75,
68			.flags = CPUIDLE_FLAG_TIMER_STOP,
69			.enter = imx6sx_enter_wait,
70			.name = "WAIT",
71			.desc = "Clock off",
72		},
73		/* WAIT + ARM power off  */
74		{
75			/*
76			 * ARM gating 31us * 5 + RBC clear 65us
77			 * and some margin for SW execution, here set it
78			 * to 300us.
79			 */
80			.exit_latency = 300,
81			.target_residency = 500,
82			.enter = imx6sx_enter_wait,
83			.name = "LOW-POWER-IDLE",
84			.desc = "ARM power off",
85		},
86	},
87	.state_count = 3,
88	.safe_state_index = 0,
89};
90
91int __init imx6sx_cpuidle_init(void)
92{
93	imx6_enable_rbc(false);
94	/*
95	 * set ARM power up/down timing to the fastest,
96	 * sw2iso and sw can be set to one 32K cycle = 31us
97	 * except for power up sw2iso which need to be
98	 * larger than LDO ramp up time.
99	 */
100	imx_gpc_set_arm_power_up_timing(2, 1);
101	imx_gpc_set_arm_power_down_timing(1, 1);
102
103	return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
104}
105