Commit 90c0ebd772713d8f80a0d27cb1b572f0f09a301c
1 parent
276a52f8b7
Exists in
master
arm:omap:am33xx: Basic cpuidle support
Add basic cpuidle support for AM33XX family of SoC. Right now only two idle states (WFI and WFI+SR) are supported. The latency/residency numbers chosen will be fine-tuned based on power measurements on actual hardware. Signed-off-by: Vaibhav Bedia <vaibhav.bedia@ti.com>
Showing 5 changed files with 241 additions and 1 deletions Side-by-side Diff
arch/arm/mach-omap2/Makefile
... | ... | @@ -81,7 +81,7 @@ |
81 | 81 | obj-$(CONFIG_ARCH_OMAP2) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o |
82 | 82 | obj-$(CONFIG_ARCH_OMAP3) += prcm.o cm2xxx_3xxx.o prm2xxx_3xxx.o \ |
83 | 83 | vc3xxx_data.o vp3xxx_data.o dvfs.o |
84 | -obj-$(CONFIG_SOC_OMAPAM33XX) += cminst33xx.o prminst33xx.o cm33xx.o | |
84 | +obj-$(CONFIG_SOC_OMAPAM33XX) += cminst33xx.o prminst33xx.o cm33xx.o cpuidle33xx.o | |
85 | 85 | |
86 | 86 | # XXX The presence of cm2xxx_3xxx.o on the line below is temporary and |
87 | 87 | # will be removed once the OMAP4 part of the codebase is converted to |
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 | + */ | |
18 | + | |
19 | +#include <linux/kernel.h> | |
20 | +#include <linux/init.h> | |
21 | +#include <linux/io.h> | |
22 | +#include <linux/platform_device.h> | |
23 | +#include <linux/cpuidle.h> | |
24 | +#include <linux/sched.h> | |
25 | +#include <asm/proc-fns.h> | |
26 | + | |
27 | +#include <plat/emif.h> | |
28 | + | |
29 | +#include "cpuidle33xx.h" | |
30 | + | |
31 | +#define AM33XX_CPUIDLE_MAX_STATES 2 | |
32 | + | |
33 | +struct am33xx_ops { | |
34 | + void (*enter) (u32 flags); | |
35 | + void (*exit) (u32 flags); | |
36 | + u32 flags; | |
37 | +}; | |
38 | + | |
39 | +/* fields in am33xx_ops.flags */ | |
40 | +#define AM33XX_CPUIDLE_FLAGS_DDR2_PWDN BIT(0) | |
41 | + | |
42 | +static struct cpuidle_driver am33xx_idle_driver = { | |
43 | + .name = "cpuidle-am33xx", | |
44 | + .owner = THIS_MODULE, | |
45 | +}; | |
46 | + | |
47 | +static DEFINE_PER_CPU(struct cpuidle_device, am33xx_cpuidle_device); | |
48 | +static void __iomem *emif_base; | |
49 | + | |
50 | +static void am33xx_save_ddr_power(int enter, bool pdown) | |
51 | +{ | |
52 | + u32 val; | |
53 | + | |
54 | + val = __raw_readl(emif_base + EMIF4_0_SDRAM_MGMT_CTRL); | |
55 | + | |
56 | + /* TODO: Choose the mode based on memory type */ | |
57 | + if (enter) | |
58 | + val = SELF_REFRESH_ENABLE(64); | |
59 | + else | |
60 | + val = SELF_REFRESH_DISABLE; | |
61 | + | |
62 | + __raw_writel(val, emif_base + EMIF4_0_SDRAM_MGMT_CTRL); | |
63 | +} | |
64 | + | |
65 | +static void am33xx_c2state_enter(u32 flags) | |
66 | +{ | |
67 | + am33xx_save_ddr_power(1, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN)); | |
68 | +} | |
69 | + | |
70 | +static void am33xx_c2state_exit(u32 flags) | |
71 | +{ | |
72 | + am33xx_save_ddr_power(0, !!(flags & AM33XX_CPUIDLE_FLAGS_DDR2_PWDN)); | |
73 | +} | |
74 | + | |
75 | +static struct am33xx_ops am33xx_states[AM33XX_CPUIDLE_MAX_STATES] = { | |
76 | + [1] = { | |
77 | + .enter = am33xx_c2state_enter, | |
78 | + .exit = am33xx_c2state_exit, | |
79 | + }, | |
80 | +}; | |
81 | + | |
82 | +/* Actual code that puts the SoC in different idle states */ | |
83 | +static int am33xx_enter_idle(struct cpuidle_device *dev, | |
84 | + struct cpuidle_state *state) | |
85 | +{ | |
86 | + struct am33xx_ops *ops = cpuidle_get_statedata(state); | |
87 | + struct timeval before, after; | |
88 | + int idle_time; | |
89 | + | |
90 | + local_irq_disable(); | |
91 | + do_gettimeofday(&before); | |
92 | + | |
93 | + if (ops && ops->enter) | |
94 | + ops->enter(ops->flags); | |
95 | + | |
96 | + /* Wait for interrupt state */ | |
97 | + cpu_do_idle(); | |
98 | + if (ops && ops->exit) | |
99 | + ops->exit(ops->flags); | |
100 | + | |
101 | + do_gettimeofday(&after); | |
102 | + local_irq_enable(); | |
103 | + idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC + | |
104 | + (after.tv_usec - before.tv_usec); | |
105 | + return idle_time; | |
106 | +} | |
107 | + | |
108 | +static int __init am33xx_cpuidle_probe(struct platform_device *pdev) | |
109 | +{ | |
110 | + int ret; | |
111 | + struct cpuidle_device *device; | |
112 | + struct am33xx_cpuidle_config *pdata = pdev->dev.platform_data; | |
113 | + | |
114 | + device = &per_cpu(am33xx_cpuidle_device, smp_processor_id()); | |
115 | + | |
116 | + if (!pdata) { | |
117 | + dev_err(&pdev->dev, "cannot get platform data\n"); | |
118 | + return -ENOENT; | |
119 | + } | |
120 | + | |
121 | + emif_base = pdata->emif_base; | |
122 | + | |
123 | + ret = cpuidle_register_driver(&am33xx_idle_driver); | |
124 | + if (ret) { | |
125 | + dev_err(&pdev->dev, "failed to register driver\n"); | |
126 | + return ret; | |
127 | + } | |
128 | + | |
129 | + /* Wait for interrupt state */ | |
130 | + device->states[0].enter = am33xx_enter_idle; | |
131 | + device->states[0].exit_latency = 1; | |
132 | + device->states[0].target_residency = 10000; | |
133 | + device->states[0].flags = CPUIDLE_FLAG_TIME_VALID; | |
134 | + strcpy(device->states[0].name, "WFI"); | |
135 | + strcpy(device->states[0].desc, "Wait for interrupt"); | |
136 | + | |
137 | + /* Wait for interrupt and DDR self refresh state */ | |
138 | + device->states[1].enter = am33xx_enter_idle; | |
139 | + device->states[1].exit_latency = 100; | |
140 | + device->states[1].target_residency = 10000; | |
141 | + device->states[1].flags = CPUIDLE_FLAG_TIME_VALID; | |
142 | + strcpy(device->states[1].name, "DDR SR"); | |
143 | + strcpy(device->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[1], &am33xx_states[1]); | |
147 | + | |
148 | + device->state_count = AM33XX_CPUIDLE_MAX_STATES; | |
149 | + | |
150 | + ret = cpuidle_register_device(device); | |
151 | + if (ret) { | |
152 | + dev_err(&pdev->dev, "failed to register device\n"); | |
153 | + cpuidle_unregister_driver(&am33xx_idle_driver); | |
154 | + return ret; | |
155 | + } | |
156 | + | |
157 | + return 0; | |
158 | +} | |
159 | + | |
160 | +static struct platform_driver am33xx_cpuidle_driver = { | |
161 | + .driver = { | |
162 | + .name = "cpuidle-am33xx", | |
163 | + .owner = THIS_MODULE, | |
164 | + }, | |
165 | +}; | |
166 | + | |
167 | +static int __init am33xx_cpuidle_init(void) | |
168 | +{ | |
169 | + return platform_driver_probe(&am33xx_cpuidle_driver, | |
170 | + am33xx_cpuidle_probe); | |
171 | +} | |
172 | +device_initcall(am33xx_cpuidle_init); |
arch/arm/mach-omap2/cpuidle33xx.h
1 | +/* | |
2 | + * TI AM33XX cpuidle platform support | |
3 | + * | |
4 | + * Copyright (C) 2011 Texas Instruments, Inc. - http://www.ti.com/ | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public License as | |
8 | + * published by the Free Software Foundation version 2. | |
9 | + * | |
10 | + * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
11 | + * kind, whether express or implied; without even the implied warranty | |
12 | + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | + * GNU General Public License for more details. | |
14 | + */ | |
15 | + | |
16 | +#ifndef _AM33XX_CPUIDLE_H | |
17 | +#define _AM33XX_CPUIDLE_H | |
18 | + | |
19 | +struct am33xx_cpuidle_config { | |
20 | + u32 ddr2_pdown; | |
21 | + void __iomem *emif_base; | |
22 | +}; | |
23 | + | |
24 | +#endif |
arch/arm/plat-omap/include/plat/am33xx.h
arch/arm/plat-omap/include/plat/emif.h
1 | +/* | |
2 | + * EMIF register definitions for TI81xx and AM33xx | |
3 | + * | |
4 | + * Copyright (C) 2011 Texas Instruments, Inc. - http://www.ti.com/ | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public License as | |
8 | + * published by the Free Software Foundation version 2. | |
9 | + * | |
10 | + * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
11 | + * kind, whether express or implied; without even the implied warranty | |
12 | + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | + * GNU General Public License for more details. | |
14 | + */ | |
15 | + | |
16 | +#ifndef __EMIF_H | |
17 | +#define __EMIF_H | |
18 | + | |
19 | +#define EMIF_MOD_ID_REV (0x0) | |
20 | +#define EMIF4_0_SDRAM_STATUS (0x04) | |
21 | +#define EMIF4_0_SDRAM_CONFIG (0x08) | |
22 | +#define EMIF4_0_SDRAM_CONFIG2 (0x0C) | |
23 | +#define EMIF4_0_SDRAM_REF_CTRL (0x10) | |
24 | +#define EMIF4_0_SDRAM_REF_CTRL_SHADOW (0x14) | |
25 | +#define EMIF4_0_SDRAM_TIM_1 (0x18) | |
26 | +#define EMIF4_0_SDRAM_TIM_1_SHADOW (0x1C) | |
27 | +#define EMIF4_0_SDRAM_TIM_2 (0x20) | |
28 | +#define EMIF4_0_SDRAM_TIM_2_SHADOW (0x24) | |
29 | +#define EMIF4_0_SDRAM_TIM_3 (0x28) | |
30 | +#define EMIF4_0_SDRAM_TIM_3_SHADOW (0x2C) | |
31 | +#define EMIF4_0_SDRAM_MGMT_CTRL (0x38) | |
32 | +#define EMIF4_0_SDRAM_MGMT_CTRL_SHD (0x3C) | |
33 | +#define EMIF4_0_DDR_PHY_CTRL_1 (0xE4) | |
34 | +#define EMIF4_0_DDR_PHY_CTRL_1_SHADOW (0xE8) | |
35 | +#define EMIF4_0_DDR_PHY_CTRL_2 (0xEC) | |
36 | +#define EMIF4_0_IODFT_TLGC (0x60) | |
37 | + | |
38 | +#define SELF_REFRESH_ENABLE(m) (0x2 << 8 | (m << 4)) | |
39 | +#define SELF_REFRESH_DISABLE (0x0 << 8) | |
40 | + | |
41 | +#endif /* __EMIF_H */ |