arm:omap:am33xx: Basic cpuidle support
[sitara-epos/sitara-epos-kernel.git] / arch / arm / mach-omap2 / cpuidle33xx.c
1 /*
2  * CPU idle for AM33XX SoCs
3  *
4  * Copyright (C) 2011 Texas Instruments Incorporated. http://www.ti.com/
5  *
6  * Derived from Davinci CPU idle code
7  * (arch/arm/mach-davinci/cpuidle.c)
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation version 2.
12  *
13  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
14  * kind, whether express or implied; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/init.h>
22 #include <linux/io.h>
23 #include <linux/platform_device.h>
24 #include <linux/cpuidle.h>
25 #include <linux/sched.h>
26 #include <asm/proc-fns.h>
28 #include <plat/emif.h>
30 #include "cpuidle33xx.h"
32 #define AM33XX_CPUIDLE_MAX_STATES       2
34 struct am33xx_ops {
35         void (*enter) (u32 flags);
36         void (*exit) (u32 flags);
37         u32 flags;
38 };
40 /* fields in am33xx_ops.flags */
41 #define AM33XX_CPUIDLE_FLAGS_DDR2_PWDN  BIT(0)
43 static struct cpuidle_driver am33xx_idle_driver = {
44         .name   = "cpuidle-am33xx",
45         .owner  = THIS_MODULE,
46 };
48 static DEFINE_PER_CPU(struct cpuidle_device, am33xx_cpuidle_device);
49 static void __iomem *emif_base;
51 static void am33xx_save_ddr_power(int enter, bool pdown)
52 {
53         u32 val;
55         val = __raw_readl(emif_base + EMIF4_0_SDRAM_MGMT_CTRL);
57         /* TODO: Choose the mode based on memory type */
58         if (enter)
59                 val = SELF_REFRESH_ENABLE(64);
60         else
61                 val = SELF_REFRESH_DISABLE;
63         __raw_writel(val, emif_base + EMIF4_0_SDRAM_MGMT_CTRL);
64 }
66 static void am33xx_c2state_enter(u32 flags)
67 {
68         am33xx_save_ddr_power(1, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN));
69 }
71 static void am33xx_c2state_exit(u32 flags)
72 {
73         am33xx_save_ddr_power(0, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN));
74 }
76 static struct am33xx_ops am33xx_states[AM33XX_CPUIDLE_MAX_STATES] = {
77         [1] = {
78                 .enter  = am33xx_c2state_enter,
79                 .exit   = am33xx_c2state_exit,
80         },
81 };
83 /* Actual code that puts the SoC in different idle states */
84 static int am33xx_enter_idle(struct cpuidle_device *dev,
85                                 struct cpuidle_driver *drv, int index)
86 {
87         struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
88         struct am33xx_ops *ops = cpuidle_get_statedata(state_usage);
89         struct timeval before, after;
90         int idle_time;
92         local_irq_disable();
93         do_gettimeofday(&before);
95         if (ops && ops->enter)
96                 ops->enter(ops->flags);
98         /* Wait for interrupt state */
99         cpu_do_idle();
100         if (ops && ops->exit)
101                 ops->exit(ops->flags);
103         do_gettimeofday(&after);
104         local_irq_enable();
105         idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
106                         (after.tv_usec - before.tv_usec);
108         dev->last_residency = idle_time;
110         return index;
113 static int __init am33xx_cpuidle_probe(struct platform_device *pdev)
115         int ret;
116         struct cpuidle_device *device;
117         struct cpuidle_driver *driver = &am33xx_idle_driver;
118         struct am33xx_cpuidle_config *pdata = pdev->dev.platform_data;
120         device = &per_cpu(am33xx_cpuidle_device, smp_processor_id());
122         if (!pdata) {
123                 dev_err(&pdev->dev, "cannot get platform data\n");
124                 return -ENOENT;
125         }
127         emif_base = pdata->emif_base;
129         /* Wait for interrupt state */
130         driver->states[0].enter = am33xx_enter_idle;
131         driver->states[0].exit_latency = 1;
132         driver->states[0].target_residency = 10000;
133         driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
134         strcpy(driver->states[0].name, "WFI");
135         strcpy(driver->states[0].desc, "Wait for interrupt");
137         /* Wait for interrupt and DDR self refresh state */
138         driver->states[1].enter = am33xx_enter_idle;
139         driver->states[1].exit_latency = 100;
140         driver->states[1].target_residency = 10000;
141         driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
142         strcpy(driver->states[1].name, "DDR SR");
143         strcpy(driver->states[1].desc, "WFI and DDR Self Refresh");
144         if (pdata->ddr2_pdown)
145                 am33xx_states[1].flags |= AM33XX_CPUIDLE_FLAGS_DDR2_PWDN;
146         cpuidle_set_statedata(&device->states_usage[1], &am33xx_states[1]);
148         device->state_count = AM33XX_CPUIDLE_MAX_STATES;
149         driver->state_count = AM33XX_CPUIDLE_MAX_STATES;
151         ret = cpuidle_register_driver(&am33xx_idle_driver);
152         if (ret) {
153                 dev_err(&pdev->dev, "failed to register driver\n");
154                 return ret;
155         }
157         ret = cpuidle_register_device(device);
158         if (ret) {
159                 dev_err(&pdev->dev, "failed to register device\n");
160                 cpuidle_unregister_driver(&am33xx_idle_driver);
161                 return ret;
162         }
164         return 0;
167 static struct platform_driver am33xx_cpuidle_driver = {
168         .driver = {
169                 .name   = "cpuidle-am33xx",
170                 .owner  = THIS_MODULE,
171         },
172 };
174 static int __init am33xx_cpuidle_init(void)
176         return platform_driver_probe(&am33xx_cpuidle_driver,
177                                                 am33xx_cpuidle_probe);
179 device_initcall(am33xx_cpuidle_init);