blob: 6fb693ffe427392fff93787303683fcb04290356 [file] [log] [blame]
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -07001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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/delay.h>
17#include <linux/init.h>
18#include <linux/io.h>
19#include <linux/slab.h>
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060020#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/platform_device.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023#include <mach/msm_iomap.h>
Praveen Chidambaram76679d42011-12-16 14:19:02 -070024#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070025#include "spm.h"
26#include "spm_driver.h"
27
28struct msm_spm_power_modes {
29 uint32_t mode;
30 bool notify_rpm;
31 uint32_t start_addr;
32
33};
34
35struct msm_spm_device {
36 struct msm_spm_driver_data reg_data;
37 struct msm_spm_power_modes *modes;
38 uint32_t num_modes;
39};
40
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060041struct msm_spm_vdd_info {
42 uint32_t cpu;
43 uint32_t vlevel;
44 int err;
45};
46
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060047static struct msm_spm_device msm_spm_l2_device;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060050
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060051static void msm_spm_smp_set_vdd(void *data)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053 struct msm_spm_device *dev;
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060054 struct msm_spm_vdd_info *info = (struct msm_spm_vdd_info *)data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060056 dev = &per_cpu(msm_cpu_spm_device, info->cpu);
57 info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
58}
59
60int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
61{
62 struct msm_spm_vdd_info info;
63 int ret;
64
65 info.cpu = cpu;
66 info.vlevel = vlevel;
67
Praveen Chidambaram78164cf2012-10-05 17:11:33 -060068 if (cpu_online(cpu)) {
69 /**
70 * We do not want to set the voltage of another core from
71 * this core, as its possible that we may race the vdd change
72 * with the SPM state machine of that core, which could also
73 * be changing the voltage of that core during power collapse.
74 * Hence, set the function to be executed on that core and block
75 * until the vdd change is complete.
76 */
77 ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd,
78 &info, true);
79 if (!ret)
80 ret = info.err;
81 } else {
82 /**
83 * Since the core is not online, it is safe to set the vdd
84 * directly.
85 */
86 msm_spm_smp_set_vdd(&info);
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060087 ret = info.err;
Praveen Chidambaram78164cf2012-10-05 17:11:33 -060088 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090 return ret;
91}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060092EXPORT_SYMBOL(msm_spm_set_vdd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093
Praveen Chidambaramebbf2122012-09-29 22:27:03 -060094unsigned int msm_spm_get_vdd(unsigned int cpu)
95{
96 struct msm_spm_device *dev;
97
98 dev = &per_cpu(msm_cpu_spm_device, cpu);
99 return msm_spm_drv_get_sts_curr_pmic_data(&dev->reg_data);
100}
101EXPORT_SYMBOL(msm_spm_get_vdd);
102
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700103static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
104 unsigned int mode, bool notify_rpm)
105{
106 uint32_t i;
107 uint32_t start_addr = 0;
108 int ret = -EINVAL;
109
110 if (mode == MSM_SPM_MODE_DISABLED) {
111 ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
112 } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
113 for (i = 0; i < dev->num_modes; i++) {
114 if ((dev->modes[i].mode == mode) &&
115 (dev->modes[i].notify_rpm == notify_rpm)) {
116 start_addr = dev->modes[i].start_addr;
117 break;
118 }
119 }
120 ret = msm_spm_drv_set_low_power_mode(&dev->reg_data,
121 start_addr);
122 }
123 return ret;
124}
125
Stephen Boyddb354112012-05-09 14:24:58 -0700126static int __devinit msm_spm_dev_init(struct msm_spm_device *dev,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 struct msm_spm_platform_data *data)
128{
129 int i, ret = -ENOMEM;
130 uint32_t offset = 0;
131
132 dev->num_modes = data->num_modes;
133 dev->modes = kmalloc(
134 sizeof(struct msm_spm_power_modes) * dev->num_modes,
135 GFP_KERNEL);
136
137 if (!dev->modes)
138 goto spm_failed_malloc;
139
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600140 dev->reg_data.ver_reg = data->ver_reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141 ret = msm_spm_drv_init(&dev->reg_data, data);
142
143 if (ret)
144 goto spm_failed_init;
145
146 for (i = 0; i < dev->num_modes; i++) {
147
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600148 /* Default offset is 0 and gets updated as we write more
149 * sequences into SPM
150 */
151 dev->modes[i].start_addr = offset;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152 ret = msm_spm_drv_write_seq_data(&dev->reg_data,
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600153 data->modes[i].cmd, &offset);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154 if (ret < 0)
155 goto spm_failed_init;
156
157 dev->modes[i].mode = data->modes[i].mode;
158 dev->modes[i].notify_rpm = data->modes[i].notify_rpm;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 }
160 msm_spm_drv_flush_seq_entry(&dev->reg_data);
161 return 0;
162
163spm_failed_init:
164 kfree(dev->modes);
165spm_failed_malloc:
166 return ret;
167}
168
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700169int msm_spm_turn_on_cpu_rail(unsigned int cpu)
170{
171 uint32_t val = 0;
172 uint32_t timeout = 0;
173 void *reg = NULL;
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800174 void *saw_bases[] = {
175 0,
176 MSM_SAW1_BASE,
177 MSM_SAW2_BASE,
178 MSM_SAW3_BASE
179 };
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700180
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800181 if (cpu == 0 || cpu >= num_possible_cpus())
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700182 return -EINVAL;
183
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800184 reg = saw_bases[cpu];
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700185
Stepan Moskovchenkoc6a603a2012-09-21 20:32:17 -0700186 if (soc_class_is_msm8960() || soc_class_is_msm8930() ||
187 soc_class_is_apq8064()) {
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800188 val = 0xA4;
189 reg += 0x14;
190 timeout = 512;
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700191 } else {
192 return -ENOSYS;
193 }
194
195 writel_relaxed(val, reg);
196 mb();
197 udelay(timeout);
198
199 return 0;
200}
201EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
202
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600203void msm_spm_reinit(void)
204{
205 unsigned int cpu;
206 for_each_possible_cpu(cpu)
207 msm_spm_drv_reinit(&per_cpu(msm_cpu_spm_device.reg_data, cpu));
208}
209EXPORT_SYMBOL(msm_spm_reinit);
210
211int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
212{
213 struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
214 return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
215}
216EXPORT_SYMBOL(msm_spm_set_low_power_mode);
217
218/* Board file init function */
219int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
220{
221 unsigned int cpu;
222 int ret = 0;
223
224 BUG_ON((nr_devs < num_possible_cpus()) || !data);
225
226 for_each_possible_cpu(cpu) {
227 struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu);
228 ret = msm_spm_dev_init(dev, &data[cpu]);
229 if (ret < 0) {
230 pr_warn("%s():failed CPU:%u ret:%d\n", __func__,
231 cpu, ret);
232 break;
233 }
234 }
235
236 return ret;
237}
238
239#ifdef CONFIG_MSM_L2_SPM
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700240
241int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm)
242{
243 return msm_spm_dev_set_low_power_mode(
244 &msm_spm_l2_device, mode, notify_rpm);
245}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600246EXPORT_SYMBOL(msm_spm_l2_set_low_power_mode);
Maheshkumar Sivasubramanian4ac23762011-11-02 10:03:06 -0600247
248void msm_spm_l2_reinit(void)
249{
250 msm_spm_drv_reinit(&msm_spm_l2_device.reg_data);
251}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600252EXPORT_SYMBOL(msm_spm_l2_reinit);
253
254int msm_spm_apcs_set_vdd(unsigned int vlevel)
255{
256 return msm_spm_drv_set_vdd(&msm_spm_l2_device.reg_data, vlevel);
257}
258EXPORT_SYMBOL(msm_spm_apcs_set_vdd);
259
260int msm_spm_apcs_set_phase(unsigned int phase_cnt)
261{
262 return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt);
263}
264EXPORT_SYMBOL(msm_spm_apcs_set_phase);
265
266/* Board file init function */
267int __init msm_spm_l2_init(struct msm_spm_platform_data *data)
268{
269 return msm_spm_dev_init(&msm_spm_l2_device, data);
270}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271#endif
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600272
Sathish Ambley86487e52012-06-11 13:46:11 -0700273static int __devinit msm_spm_dev_probe(struct platform_device *pdev)
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600274{
275 int ret = 0;
276 int cpu = 0;
277 int i = 0;
278 struct device_node *node = pdev->dev.of_node;
279 struct msm_spm_platform_data spm_data;
280 char *key = NULL;
281 uint32_t val = 0;
282 struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR];
283 size_t len = 0;
284 struct msm_spm_device *dev = NULL;
285 struct resource *res = NULL;
286 uint32_t mode_count = 0;
287
288 struct spm_of {
289 char *key;
290 uint32_t id;
291 };
292
293 struct spm_of spm_of_data[] = {
294 {"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG},
295 {"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW2_AVS_CTL},
296 {"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW2_AVS_HYSTERESIS},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600297 {"qcom,saw2-avs-limit", MSM_SPM_REG_SAW2_AVS_LIMIT},
Praveen Chidambaramce73c372012-08-22 11:50:34 -0600298 {"qcom,saw2-avs-dly", MSM_SPM_REG_SAW2_AVS_DLY},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600299 {"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY},
Praveen Chidambaramce73c372012-08-22 11:50:34 -0600300 {"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600301 {"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0},
302 {"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1},
303 {"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2},
304 {"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3},
305 {"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4},
306 {"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5},
307 {"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6},
308 {"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7},
309 };
310
311 struct mode_of {
312 char *key;
313 uint32_t id;
314 uint32_t notify_rpm;
315 };
316
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600317 struct mode_of of_cpu_modes[] = {
318 {"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0},
319 {"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_POWER_RETENTION, 0},
320 {"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0},
321 {"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600322 };
323
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600324 struct mode_of of_l2_modes[] = {
325 {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 1},
326 {"qcom,saw2-spm-cmd-gdhs", MSM_SPM_L2_MODE_GDHS, 1},
327 {"qcom,saw2-spm-cmd-pc", MSM_SPM_L2_MODE_POWER_COLLAPSE, 1},
328 };
329
330 struct mode_of *mode_of_data;
331 int num_modes;
332
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600333 memset(&spm_data, 0, sizeof(struct msm_spm_platform_data));
334 memset(&modes, 0,
335 (MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
336
337 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
338 if (!res)
339 goto fail;
340
341 spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
342 resource_size(res));
343 if (!spm_data.reg_base_addr)
344 return -ENOMEM;
345
346 key = "qcom,core-id";
347 ret = of_property_read_u32(node, key, &val);
348 if (ret)
349 goto fail;
350 cpu = val;
351
352 key = "qcom,saw2-ver-reg";
353 ret = of_property_read_u32(node, key, &val);
354 if (ret)
355 goto fail;
356 spm_data.ver_reg = val;
357
358 key = "qcom,vctl-timeout-us";
359 ret = of_property_read_u32(node, key, &val);
360 if (!ret)
361 spm_data.vctl_timeout_us = val;
362
363 /* optional */
364 key = "qcom,vctl-port";
365 ret = of_property_read_u32(node, key, &val);
366 if (!ret)
367 spm_data.vctl_port = val;
368
369 /* optional */
370 key = "qcom,phase-port";
371 ret = of_property_read_u32(node, key, &val);
372 if (!ret)
373 spm_data.phase_port = val;
374
375 for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
376 ret = of_property_read_u32(node, spm_of_data[i].key, &val);
377 if (ret)
378 continue;
379 spm_data.reg_init_values[spm_of_data[i].id] = val;
380 }
381
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600382 /*
383 * Device with id 0..NR_CPUS are SPM for apps cores
384 * Device with id 0xFFFF is for L2 SPM.
385 */
386 if (cpu >= 0 && cpu < num_possible_cpus()) {
387 mode_of_data = of_cpu_modes;
388 num_modes = ARRAY_SIZE(of_cpu_modes);
389 dev = &per_cpu(msm_cpu_spm_device, cpu);
390
391 } else {
392 mode_of_data = of_l2_modes;
393 num_modes = ARRAY_SIZE(of_l2_modes);
394 dev = &msm_spm_l2_device;
395 }
396
397 for (i = 0; i < num_modes; i++) {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600398 key = mode_of_data[i].key;
399 modes[mode_count].cmd =
400 (uint8_t *)of_get_property(node, key, &len);
401 if (!modes[mode_count].cmd)
402 continue;
403 modes[mode_count].mode = mode_of_data[i].id;
404 modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm;
405 mode_count++;
406 }
407
408 spm_data.modes = modes;
409 spm_data.num_modes = mode_count;
410
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600411 ret = msm_spm_dev_init(dev, &spm_data);
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600412
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600413 if (ret < 0)
414 pr_warn("%s():failed core-id:%u ret:%d\n", __func__, cpu, ret);
415
416 return ret;
417
418fail:
419 pr_err("%s: Failed reading node=%s, key=%s\n",
420 __func__, node->full_name, key);
421 return -EFAULT;
422}
423
Sathish Ambley86487e52012-06-11 13:46:11 -0700424static struct of_device_id msm_spm_match_table[] = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600425 {.compatible = "qcom,spm-v2"},
426 {},
427};
428
Sathish Ambley86487e52012-06-11 13:46:11 -0700429static struct platform_driver msm_spm_device_driver = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600430 .probe = msm_spm_dev_probe,
431 .driver = {
432 .name = "spm-v2",
433 .owner = THIS_MODULE,
434 .of_match_table = msm_spm_match_table,
435 },
436};
437
438int __init msm_spm_device_init(void)
439{
440 return platform_driver_register(&msm_spm_device_driver);
441}