blob: a1f5ff5595e6e5ec08dbbadb794aa03bbe3ad8d4 [file] [log] [blame]
Praveen Chidambaram85b7b282012-04-16 13:45:15 -06001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/init.h>
17#include <linux/slab.h>
18#include <linux/platform_device.h>
19#include <linux/of.h>
20#include <mach/mpm.h>
21#include "rpm_resources.h"
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060022#include "pm.h"
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060023
24static struct msm_rpmrs_level *msm_lpm_levels;
25static int msm_lpm_level_count;
26
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060027static int msm_lpm_enter_sleep(uint32_t sclk_count, void *limits,
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060028 bool from_idle, bool notify_rpm)
29{
30 /* TODO */
31 return 0;
32}
33
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060034static void msm_lpm_exit_sleep(void *limits, bool from_idle,
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060035 bool notify_rpm, bool collapsed)
36{
37 /* TODO */
38 return;
39}
40
41static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits,
42 bool irqs_detect, bool gpio_detect)
43{
44 /* TODO */
45 return true;
46}
47
48void msm_rpmrs_show_resources(void)
49{
50 /* TODO */
51 return;
52}
53
Stephen Boyd3f4bac22012-05-30 10:03:13 -070054s32 msm_cpuidle_get_deep_idle_latency(void)
55{
56 int i;
57 struct msm_rpmrs_level *level = msm_lpm_levels, *best = level;
58
59 if (!level)
60 return 0;
61
62 for (i = 0; i < msm_lpm_level_count; i++, level++) {
63 if (!level->available)
64 continue;
65 if (level->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
66 continue;
67 /* Pick the first power collapse mode by default */
68 if (best->sleep_mode != MSM_PM_SLEEP_MODE_POWER_COLLAPSE)
69 best = level;
70 /* Find the lowest latency for power collapse */
71 if (level->latency_us < best->latency_us)
72 best = level;
73 }
74 return best->latency_us - 1;
75}
76
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060077static void *msm_lpm_lowest_limits(bool from_idle,
78 enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
79 uint32_t sleep_us, uint32_t *power)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060080{
81 unsigned int cpu = smp_processor_id();
82 struct msm_rpmrs_level *best_level = NULL;
83 bool irqs_detectable = false;
84 bool gpio_detectable = false;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060085 uint32_t pwr;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060086 int i;
87
88 if (!msm_lpm_levels)
89 return NULL;
90
91 if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
92 irqs_detectable = msm_mpm_irqs_detectable(from_idle);
93 gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle);
94 }
95
96 for (i = 0; i < msm_lpm_level_count; i++) {
97 struct msm_rpmrs_level *level = &msm_lpm_levels[i];
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060098
99 if (!level->available)
100 continue;
101
102 if (sleep_mode != level->sleep_mode)
103 continue;
104
105 if (latency_us < level->latency_us)
106 continue;
107
108 if (!msm_rpmrs_irqs_detectable(&level->rs_limits,
109 irqs_detectable, gpio_detectable))
110 continue;
111
112 if (sleep_us <= 1) {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600113 pwr = level->energy_overhead;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600114 } else if (sleep_us <= level->time_overhead_us) {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600115 pwr = level->energy_overhead / sleep_us;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600116 } else if ((sleep_us >> 10) > level->time_overhead_us) {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600117 pwr = level->steady_state_power;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600118 } else {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600119 pwr = level->steady_state_power;
120 pwr -= (level->time_overhead_us *
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600121 level->steady_state_power)/sleep_us;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600122 pwr += level->energy_overhead / sleep_us;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600123 }
124
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600125 if (!best_level || best_level->rs_limits.power[cpu] >= pwr) {
126
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600127 level->rs_limits.latency_us[cpu] = level->latency_us;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600128 level->rs_limits.power[cpu] = pwr;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600129 best_level = level;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600130
131 if (power)
132 *power = pwr;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600133 }
134 }
135
136 return best_level ? &best_level->rs_limits : NULL;
137}
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600138static struct msm_pm_sleep_ops msm_lpm_ops = {
139 .lowest_limits = msm_lpm_lowest_limits,
140 .enter_sleep = msm_lpm_enter_sleep,
141 .exit_sleep = msm_lpm_exit_sleep,
142};
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600143
144static int __devinit msm_lpm_levels_probe(struct platform_device *pdev)
145{
146 struct msm_rpmrs_level *levels = NULL;
147 struct msm_rpmrs_level *level = NULL;
148 struct device_node *node = NULL;
149 char *key = NULL;
150 uint32_t val = 0;
151 int ret = 0;
152 uint32_t num_levels = 0;
153 int idx = 0;
154
155 for_each_child_of_node(pdev->dev.of_node, node)
156 num_levels++;
157
158 levels = kzalloc(num_levels * sizeof(struct msm_rpmrs_level),
159 GFP_KERNEL);
160 if (!levels)
161 return -ENOMEM;
162
163 for_each_child_of_node(pdev->dev.of_node, node) {
164 level = &levels[idx++];
165 level->available = false;
166
167 key = "qcom,mode";
168 ret = of_property_read_u32(node, key, &val);
169 if (ret)
170 goto fail;
171 level->sleep_mode = val;
172
173 key = "qcom,xo";
174 ret = of_property_read_u32(node, key, &val);
175 if (ret)
176 goto fail;
177 level->rs_limits.pxo = val;
178
179 key = "qcom,l2";
180 ret = of_property_read_u32(node, key, &val);
181 if (ret)
182 goto fail;
183 level->rs_limits.l2_cache = val;
184
185 key = "qcom,vdd-dig-upper-bound";
186 ret = of_property_read_u32(node, key, &val);
187 if (ret)
188 goto fail;
189 level->rs_limits.vdd_dig_upper_bound = val;
190
191 key = "qcom,vdd-dig-lower-bound";
192 ret = of_property_read_u32(node, key, &val);
193 if (ret)
194 goto fail;
195 level->rs_limits.vdd_dig = val;
196
197 key = "qcom,vdd-mem-upper-bound";
198 ret = of_property_read_u32(node, key, &val);
199 if (ret)
200 goto fail;
201 level->rs_limits.vdd_mem_upper_bound = val;
202
203 key = "qcom,vdd-mem-lower-bound";
204 ret = of_property_read_u32(node, key, &val);
205 if (ret)
206 goto fail;
207 level->rs_limits.vdd_mem = val;
208
209 key = "qcom,latency-us";
210 ret = of_property_read_u32(node, key, &val);
211 if (ret)
212 goto fail;
213 level->latency_us = val;
214
215 key = "qcom,ss-power";
216 ret = of_property_read_u32(node, key, &val);
217 if (ret)
218 goto fail;
219 level->steady_state_power = val;
220
221 key = "qcom,energy-overhead";
222 ret = of_property_read_u32(node, key, &val);
223 if (ret)
224 goto fail;
225 level->energy_overhead = val;
226
227 key = "qcom,time-overhead";
228 ret = of_property_read_u32(node, key, &val);
229 if (ret)
230 goto fail;
231 level->time_overhead_us = val;
232
233 level->available = true;
234 }
235
236 msm_lpm_levels = levels;
237 msm_lpm_level_count = idx;
238
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600239 msm_pm_set_sleep_ops(&msm_lpm_ops);
240
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600241 return 0;
242fail:
243 pr_err("%s: Error in name %s key %s\n", __func__, node->full_name, key);
244 kfree(levels);
245 return -EFAULT;
246}
247
248static struct of_device_id msm_lpm_levels_match_table[] = {
249 {.compatible = "qcom,lpm-levels"},
250 {},
251};
252
253static struct platform_driver msm_lpm_levels_driver = {
254 .probe = msm_lpm_levels_probe,
255 .driver = {
256 .name = "lpm-levels",
257 .owner = THIS_MODULE,
258 .of_match_table = msm_lpm_levels_match_table,
259 },
260};
261
262static int __init msm_lpm_levels_module_init(void)
263{
264 return platform_driver_register(&msm_lpm_levels_driver);
265}
266late_initcall(msm_lpm_levels_module_init);