blob: 80b82cbcf6219a651f158f7fff3ef804de54ee1e [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
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060054static void *msm_lpm_lowest_limits(bool from_idle,
55 enum msm_pm_sleep_mode sleep_mode, uint32_t latency_us,
56 uint32_t sleep_us, uint32_t *power)
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060057{
58 unsigned int cpu = smp_processor_id();
59 struct msm_rpmrs_level *best_level = NULL;
60 bool irqs_detectable = false;
61 bool gpio_detectable = false;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060062 uint32_t pwr;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060063 int i;
64
65 if (!msm_lpm_levels)
66 return NULL;
67
68 if (sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) {
69 irqs_detectable = msm_mpm_irqs_detectable(from_idle);
70 gpio_detectable = msm_mpm_gpio_irqs_detectable(from_idle);
71 }
72
73 for (i = 0; i < msm_lpm_level_count; i++) {
74 struct msm_rpmrs_level *level = &msm_lpm_levels[i];
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060075
76 if (!level->available)
77 continue;
78
79 if (sleep_mode != level->sleep_mode)
80 continue;
81
82 if (latency_us < level->latency_us)
83 continue;
84
85 if (!msm_rpmrs_irqs_detectable(&level->rs_limits,
86 irqs_detectable, gpio_detectable))
87 continue;
88
89 if (sleep_us <= 1) {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060090 pwr = level->energy_overhead;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060091 } else if (sleep_us <= level->time_overhead_us) {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060092 pwr = level->energy_overhead / sleep_us;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060093 } else if ((sleep_us >> 10) > level->time_overhead_us) {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060094 pwr = level->steady_state_power;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060095 } else {
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060096 pwr = level->steady_state_power;
97 pwr -= (level->time_overhead_us *
Praveen Chidambaram85b7b282012-04-16 13:45:15 -060098 level->steady_state_power)/sleep_us;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -060099 pwr += level->energy_overhead / sleep_us;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600100 }
101
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600102 if (!best_level || best_level->rs_limits.power[cpu] >= pwr) {
103
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600104 level->rs_limits.latency_us[cpu] = level->latency_us;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600105 level->rs_limits.power[cpu] = pwr;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600106 best_level = level;
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600107
108 if (power)
109 *power = pwr;
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600110 }
111 }
112
113 return best_level ? &best_level->rs_limits : NULL;
114}
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600115static struct msm_pm_sleep_ops msm_lpm_ops = {
116 .lowest_limits = msm_lpm_lowest_limits,
117 .enter_sleep = msm_lpm_enter_sleep,
118 .exit_sleep = msm_lpm_exit_sleep,
119};
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600120
121static int __devinit msm_lpm_levels_probe(struct platform_device *pdev)
122{
123 struct msm_rpmrs_level *levels = NULL;
124 struct msm_rpmrs_level *level = NULL;
125 struct device_node *node = NULL;
126 char *key = NULL;
127 uint32_t val = 0;
128 int ret = 0;
129 uint32_t num_levels = 0;
130 int idx = 0;
131
132 for_each_child_of_node(pdev->dev.of_node, node)
133 num_levels++;
134
135 levels = kzalloc(num_levels * sizeof(struct msm_rpmrs_level),
136 GFP_KERNEL);
137 if (!levels)
138 return -ENOMEM;
139
140 for_each_child_of_node(pdev->dev.of_node, node) {
141 level = &levels[idx++];
142 level->available = false;
143
144 key = "qcom,mode";
145 ret = of_property_read_u32(node, key, &val);
146 if (ret)
147 goto fail;
148 level->sleep_mode = val;
149
150 key = "qcom,xo";
151 ret = of_property_read_u32(node, key, &val);
152 if (ret)
153 goto fail;
154 level->rs_limits.pxo = val;
155
156 key = "qcom,l2";
157 ret = of_property_read_u32(node, key, &val);
158 if (ret)
159 goto fail;
160 level->rs_limits.l2_cache = val;
161
162 key = "qcom,vdd-dig-upper-bound";
163 ret = of_property_read_u32(node, key, &val);
164 if (ret)
165 goto fail;
166 level->rs_limits.vdd_dig_upper_bound = val;
167
168 key = "qcom,vdd-dig-lower-bound";
169 ret = of_property_read_u32(node, key, &val);
170 if (ret)
171 goto fail;
172 level->rs_limits.vdd_dig = val;
173
174 key = "qcom,vdd-mem-upper-bound";
175 ret = of_property_read_u32(node, key, &val);
176 if (ret)
177 goto fail;
178 level->rs_limits.vdd_mem_upper_bound = val;
179
180 key = "qcom,vdd-mem-lower-bound";
181 ret = of_property_read_u32(node, key, &val);
182 if (ret)
183 goto fail;
184 level->rs_limits.vdd_mem = val;
185
186 key = "qcom,latency-us";
187 ret = of_property_read_u32(node, key, &val);
188 if (ret)
189 goto fail;
190 level->latency_us = val;
191
192 key = "qcom,ss-power";
193 ret = of_property_read_u32(node, key, &val);
194 if (ret)
195 goto fail;
196 level->steady_state_power = val;
197
198 key = "qcom,energy-overhead";
199 ret = of_property_read_u32(node, key, &val);
200 if (ret)
201 goto fail;
202 level->energy_overhead = val;
203
204 key = "qcom,time-overhead";
205 ret = of_property_read_u32(node, key, &val);
206 if (ret)
207 goto fail;
208 level->time_overhead_us = val;
209
210 level->available = true;
211 }
212
213 msm_lpm_levels = levels;
214 msm_lpm_level_count = idx;
215
Mahesh Sivasubramanian6d06e3a2012-05-16 13:41:07 -0600216 msm_pm_set_sleep_ops(&msm_lpm_ops);
217
Praveen Chidambaram85b7b282012-04-16 13:45:15 -0600218 return 0;
219fail:
220 pr_err("%s: Error in name %s key %s\n", __func__, node->full_name, key);
221 kfree(levels);
222 return -EFAULT;
223}
224
225static struct of_device_id msm_lpm_levels_match_table[] = {
226 {.compatible = "qcom,lpm-levels"},
227 {},
228};
229
230static struct platform_driver msm_lpm_levels_driver = {
231 .probe = msm_lpm_levels_probe,
232 .driver = {
233 .name = "lpm-levels",
234 .owner = THIS_MODULE,
235 .of_match_table = msm_lpm_levels_match_table,
236 },
237};
238
239static int __init msm_lpm_levels_module_init(void)
240{
241 return platform_driver_register(&msm_lpm_levels_driver);
242}
243late_initcall(msm_lpm_levels_module_init);