blob: 48cf3f7a1c724bb595482720c8f9ac1d05d8cdcc [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"
22
23static struct msm_rpmrs_level *msm_lpm_levels;
24static int msm_lpm_level_count;
25
26int msm_rpmrs_enter_sleep(uint32_t sclk_count, struct msm_rpmrs_limits *limits,
27 bool from_idle, bool notify_rpm)
28{
29 /* TODO */
30 return 0;
31}
32
33void msm_rpmrs_exit_sleep(struct msm_rpmrs_limits *limits, bool from_idle,
34 bool notify_rpm, bool collapsed)
35{
36 /* TODO */
37 return;
38}
39
40static bool msm_rpmrs_irqs_detectable(struct msm_rpmrs_limits *limits,
41 bool irqs_detect, bool gpio_detect)
42{
43 /* TODO */
44 return true;
45}
46
47void msm_rpmrs_show_resources(void)
48{
49 /* TODO */
50 return;
51}
52
53struct msm_rpmrs_limits *msm_rpmrs_lowest_limits(
54 bool from_idle, enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
55 uint32_t sleep_us)
56{
57 unsigned int cpu = smp_processor_id();
58 struct msm_rpmrs_level *best_level = NULL;
59 bool irqs_detectable = false;
60 bool gpio_detectable = false;
61 int i;
62
63 if (!msm_lpm_levels)
64 return NULL;
65
66 if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
67 irqs_detectable = msm_mpm_irqs_detectable(from_idle);
68 gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle);
69 }
70
71 for (i = 0; i < msm_lpm_level_count; i++) {
72 struct msm_rpmrs_level *level = &msm_lpm_levels[i];
73 uint32_t power;
74
75 if (!level->available)
76 continue;
77
78 if (sleep_mode != level->sleep_mode)
79 continue;
80
81 if (latency_us < level->latency_us)
82 continue;
83
84 if (!msm_rpmrs_irqs_detectable(&level->rs_limits,
85 irqs_detectable, gpio_detectable))
86 continue;
87
88 if (sleep_us <= 1) {
89 power = level->energy_overhead;
90 } else if (sleep_us <= level->time_overhead_us) {
91 power = level->energy_overhead / sleep_us;
92 } else if ((sleep_us >> 10) > level->time_overhead_us) {
93 power = level->steady_state_power;
94 } else {
95 power = level->steady_state_power;
96 power -= (level->time_overhead_us *
97 level->steady_state_power)/sleep_us;
98 power += level->energy_overhead / sleep_us;
99 }
100
101 if (!best_level ||
102 best_level->rs_limits.power[cpu] >= power) {
103 level->rs_limits.latency_us[cpu] = level->latency_us;
104 level->rs_limits.power[cpu] = power;
105 best_level = level;
106 }
107 }
108
109 return best_level ? &best_level->rs_limits : NULL;
110}
111
112static int __devinit msm_lpm_levels_probe(struct platform_device *pdev)
113{
114 struct msm_rpmrs_level *levels = NULL;
115 struct msm_rpmrs_level *level = NULL;
116 struct device_node *node = NULL;
117 char *key = NULL;
118 uint32_t val = 0;
119 int ret = 0;
120 uint32_t num_levels = 0;
121 int idx = 0;
122
123 for_each_child_of_node(pdev->dev.of_node, node)
124 num_levels++;
125
126 levels = kzalloc(num_levels * sizeof(struct msm_rpmrs_level),
127 GFP_KERNEL);
128 if (!levels)
129 return -ENOMEM;
130
131 for_each_child_of_node(pdev->dev.of_node, node) {
132 level = &levels[idx++];
133 level->available = false;
134
135 key = "qcom,mode";
136 ret = of_property_read_u32(node, key, &val);
137 if (ret)
138 goto fail;
139 level->sleep_mode = val;
140
141 key = "qcom,xo";
142 ret = of_property_read_u32(node, key, &val);
143 if (ret)
144 goto fail;
145 level->rs_limits.pxo = val;
146
147 key = "qcom,l2";
148 ret = of_property_read_u32(node, key, &val);
149 if (ret)
150 goto fail;
151 level->rs_limits.l2_cache = val;
152
153 key = "qcom,vdd-dig-upper-bound";
154 ret = of_property_read_u32(node, key, &val);
155 if (ret)
156 goto fail;
157 level->rs_limits.vdd_dig_upper_bound = val;
158
159 key = "qcom,vdd-dig-lower-bound";
160 ret = of_property_read_u32(node, key, &val);
161 if (ret)
162 goto fail;
163 level->rs_limits.vdd_dig = val;
164
165 key = "qcom,vdd-mem-upper-bound";
166 ret = of_property_read_u32(node, key, &val);
167 if (ret)
168 goto fail;
169 level->rs_limits.vdd_mem_upper_bound = val;
170
171 key = "qcom,vdd-mem-lower-bound";
172 ret = of_property_read_u32(node, key, &val);
173 if (ret)
174 goto fail;
175 level->rs_limits.vdd_mem = val;
176
177 key = "qcom,latency-us";
178 ret = of_property_read_u32(node, key, &val);
179 if (ret)
180 goto fail;
181 level->latency_us = val;
182
183 key = "qcom,ss-power";
184 ret = of_property_read_u32(node, key, &val);
185 if (ret)
186 goto fail;
187 level->steady_state_power = val;
188
189 key = "qcom,energy-overhead";
190 ret = of_property_read_u32(node, key, &val);
191 if (ret)
192 goto fail;
193 level->energy_overhead = val;
194
195 key = "qcom,time-overhead";
196 ret = of_property_read_u32(node, key, &val);
197 if (ret)
198 goto fail;
199 level->time_overhead_us = val;
200
201 level->available = true;
202 }
203
204 msm_lpm_levels = levels;
205 msm_lpm_level_count = idx;
206
207 return 0;
208fail:
209 pr_err("%s: Error in name %s key %s\n", __func__, node->full_name, key);
210 kfree(levels);
211 return -EFAULT;
212}
213
214static struct of_device_id msm_lpm_levels_match_table[] = {
215 {.compatible = "qcom,lpm-levels"},
216 {},
217};
218
219static struct platform_driver msm_lpm_levels_driver = {
220 .probe = msm_lpm_levels_probe,
221 .driver = {
222 .name = "lpm-levels",
223 .owner = THIS_MODULE,
224 .of_match_table = msm_lpm_levels_match_table,
225 },
226};
227
228static int __init msm_lpm_levels_module_init(void)
229{
230 return platform_driver_register(&msm_lpm_levels_driver);
231}
232late_initcall(msm_lpm_levels_module_init);