blob: 2cbed946f1371f95b54722ff8069e91ace692d7b [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
51/* Must be called on the same cpu as the one being set to */
52static void msm_spm_smp_set_vdd(void *data)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054 struct msm_spm_device *dev;
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060055 struct msm_spm_vdd_info *info = (struct msm_spm_vdd_info *)data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056
Praveen Chidambaram6a8fb3b2012-09-16 14:54:35 -060057 dev = &per_cpu(msm_cpu_spm_device, info->cpu);
58 info->err = msm_spm_drv_set_vdd(&dev->reg_data, info->vlevel);
59}
60
61int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
62{
63 struct msm_spm_vdd_info info;
64 int ret;
65
66 info.cpu = cpu;
67 info.vlevel = vlevel;
68
69 /* Set to true to block on vdd change */
70 ret = smp_call_function_single(cpu, msm_spm_smp_set_vdd, &info, true);
71 if (!ret)
72 ret = info.err;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074 return ret;
75}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060076EXPORT_SYMBOL(msm_spm_set_vdd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077
Praveen Chidambaramebbf2122012-09-29 22:27:03 -060078unsigned int msm_spm_get_vdd(unsigned int cpu)
79{
80 struct msm_spm_device *dev;
81
82 dev = &per_cpu(msm_cpu_spm_device, cpu);
83 return msm_spm_drv_get_sts_curr_pmic_data(&dev->reg_data);
84}
85EXPORT_SYMBOL(msm_spm_get_vdd);
86
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070087static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
88 unsigned int mode, bool notify_rpm)
89{
90 uint32_t i;
91 uint32_t start_addr = 0;
92 int ret = -EINVAL;
93
94 if (mode == MSM_SPM_MODE_DISABLED) {
95 ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
96 } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
97 for (i = 0; i < dev->num_modes; i++) {
98 if ((dev->modes[i].mode == mode) &&
99 (dev->modes[i].notify_rpm == notify_rpm)) {
100 start_addr = dev->modes[i].start_addr;
101 break;
102 }
103 }
104 ret = msm_spm_drv_set_low_power_mode(&dev->reg_data,
105 start_addr);
106 }
107 return ret;
108}
109
Stephen Boyddb354112012-05-09 14:24:58 -0700110static int __devinit msm_spm_dev_init(struct msm_spm_device *dev,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 struct msm_spm_platform_data *data)
112{
113 int i, ret = -ENOMEM;
114 uint32_t offset = 0;
115
116 dev->num_modes = data->num_modes;
117 dev->modes = kmalloc(
118 sizeof(struct msm_spm_power_modes) * dev->num_modes,
119 GFP_KERNEL);
120
121 if (!dev->modes)
122 goto spm_failed_malloc;
123
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600124 dev->reg_data.ver_reg = data->ver_reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 ret = msm_spm_drv_init(&dev->reg_data, data);
126
127 if (ret)
128 goto spm_failed_init;
129
130 for (i = 0; i < dev->num_modes; i++) {
131
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600132 /* Default offset is 0 and gets updated as we write more
133 * sequences into SPM
134 */
135 dev->modes[i].start_addr = offset;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136 ret = msm_spm_drv_write_seq_data(&dev->reg_data,
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600137 data->modes[i].cmd, &offset);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138 if (ret < 0)
139 goto spm_failed_init;
140
141 dev->modes[i].mode = data->modes[i].mode;
142 dev->modes[i].notify_rpm = data->modes[i].notify_rpm;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143 }
144 msm_spm_drv_flush_seq_entry(&dev->reg_data);
145 return 0;
146
147spm_failed_init:
148 kfree(dev->modes);
149spm_failed_malloc:
150 return ret;
151}
152
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700153int msm_spm_turn_on_cpu_rail(unsigned int cpu)
154{
155 uint32_t val = 0;
156 uint32_t timeout = 0;
157 void *reg = NULL;
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800158 void *saw_bases[] = {
159 0,
160 MSM_SAW1_BASE,
161 MSM_SAW2_BASE,
162 MSM_SAW3_BASE
163 };
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700164
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800165 if (cpu == 0 || cpu >= num_possible_cpus())
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700166 return -EINVAL;
167
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800168 reg = saw_bases[cpu];
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700169
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700170 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
Jay Chokshibae1cb52012-09-20 14:35:17 -0700171 cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab() ||
172 cpu_is_apq8064ab()) {
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800173 val = 0xA4;
174 reg += 0x14;
175 timeout = 512;
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700176 } else {
177 return -ENOSYS;
178 }
179
180 writel_relaxed(val, reg);
181 mb();
182 udelay(timeout);
183
184 return 0;
185}
186EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
187
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600188void msm_spm_reinit(void)
189{
190 unsigned int cpu;
191 for_each_possible_cpu(cpu)
192 msm_spm_drv_reinit(&per_cpu(msm_cpu_spm_device.reg_data, cpu));
193}
194EXPORT_SYMBOL(msm_spm_reinit);
195
196int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
197{
198 struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
199 return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
200}
201EXPORT_SYMBOL(msm_spm_set_low_power_mode);
202
203/* Board file init function */
204int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
205{
206 unsigned int cpu;
207 int ret = 0;
208
209 BUG_ON((nr_devs < num_possible_cpus()) || !data);
210
211 for_each_possible_cpu(cpu) {
212 struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu);
213 ret = msm_spm_dev_init(dev, &data[cpu]);
214 if (ret < 0) {
215 pr_warn("%s():failed CPU:%u ret:%d\n", __func__,
216 cpu, ret);
217 break;
218 }
219 }
220
221 return ret;
222}
223
224#ifdef CONFIG_MSM_L2_SPM
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225
226int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm)
227{
228 return msm_spm_dev_set_low_power_mode(
229 &msm_spm_l2_device, mode, notify_rpm);
230}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600231EXPORT_SYMBOL(msm_spm_l2_set_low_power_mode);
Maheshkumar Sivasubramanian4ac23762011-11-02 10:03:06 -0600232
233void msm_spm_l2_reinit(void)
234{
235 msm_spm_drv_reinit(&msm_spm_l2_device.reg_data);
236}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600237EXPORT_SYMBOL(msm_spm_l2_reinit);
238
239int msm_spm_apcs_set_vdd(unsigned int vlevel)
240{
241 return msm_spm_drv_set_vdd(&msm_spm_l2_device.reg_data, vlevel);
242}
243EXPORT_SYMBOL(msm_spm_apcs_set_vdd);
244
245int msm_spm_apcs_set_phase(unsigned int phase_cnt)
246{
247 return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt);
248}
249EXPORT_SYMBOL(msm_spm_apcs_set_phase);
250
251/* Board file init function */
252int __init msm_spm_l2_init(struct msm_spm_platform_data *data)
253{
254 return msm_spm_dev_init(&msm_spm_l2_device, data);
255}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256#endif
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600257
Sathish Ambley86487e52012-06-11 13:46:11 -0700258static int __devinit msm_spm_dev_probe(struct platform_device *pdev)
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600259{
260 int ret = 0;
261 int cpu = 0;
262 int i = 0;
263 struct device_node *node = pdev->dev.of_node;
264 struct msm_spm_platform_data spm_data;
265 char *key = NULL;
266 uint32_t val = 0;
267 struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR];
268 size_t len = 0;
269 struct msm_spm_device *dev = NULL;
270 struct resource *res = NULL;
271 uint32_t mode_count = 0;
272
273 struct spm_of {
274 char *key;
275 uint32_t id;
276 };
277
278 struct spm_of spm_of_data[] = {
279 {"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG},
280 {"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW2_AVS_CTL},
281 {"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW2_AVS_HYSTERESIS},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600282 {"qcom,saw2-avs-limit", MSM_SPM_REG_SAW2_AVS_LIMIT},
Praveen Chidambaramce73c372012-08-22 11:50:34 -0600283 {"qcom,saw2-avs-dly", MSM_SPM_REG_SAW2_AVS_DLY},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600284 {"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY},
Praveen Chidambaramce73c372012-08-22 11:50:34 -0600285 {"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600286 {"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0},
287 {"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1},
288 {"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2},
289 {"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3},
290 {"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4},
291 {"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5},
292 {"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6},
293 {"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7},
294 };
295
296 struct mode_of {
297 char *key;
298 uint32_t id;
299 uint32_t notify_rpm;
300 };
301
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600302 struct mode_of of_cpu_modes[] = {
303 {"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0},
304 {"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_POWER_RETENTION, 0},
305 {"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0},
306 {"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600307 };
308
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600309 struct mode_of of_l2_modes[] = {
310 {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 1},
311 {"qcom,saw2-spm-cmd-gdhs", MSM_SPM_L2_MODE_GDHS, 1},
312 {"qcom,saw2-spm-cmd-pc", MSM_SPM_L2_MODE_POWER_COLLAPSE, 1},
313 };
314
315 struct mode_of *mode_of_data;
316 int num_modes;
317
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600318 memset(&spm_data, 0, sizeof(struct msm_spm_platform_data));
319 memset(&modes, 0,
320 (MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
321
322 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
323 if (!res)
324 goto fail;
325
326 spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
327 resource_size(res));
328 if (!spm_data.reg_base_addr)
329 return -ENOMEM;
330
331 key = "qcom,core-id";
332 ret = of_property_read_u32(node, key, &val);
333 if (ret)
334 goto fail;
335 cpu = val;
336
337 key = "qcom,saw2-ver-reg";
338 ret = of_property_read_u32(node, key, &val);
339 if (ret)
340 goto fail;
341 spm_data.ver_reg = val;
342
343 key = "qcom,vctl-timeout-us";
344 ret = of_property_read_u32(node, key, &val);
345 if (!ret)
346 spm_data.vctl_timeout_us = val;
347
348 /* optional */
349 key = "qcom,vctl-port";
350 ret = of_property_read_u32(node, key, &val);
351 if (!ret)
352 spm_data.vctl_port = val;
353
354 /* optional */
355 key = "qcom,phase-port";
356 ret = of_property_read_u32(node, key, &val);
357 if (!ret)
358 spm_data.phase_port = val;
359
360 for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
361 ret = of_property_read_u32(node, spm_of_data[i].key, &val);
362 if (ret)
363 continue;
364 spm_data.reg_init_values[spm_of_data[i].id] = val;
365 }
366
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600367 /*
368 * Device with id 0..NR_CPUS are SPM for apps cores
369 * Device with id 0xFFFF is for L2 SPM.
370 */
371 if (cpu >= 0 && cpu < num_possible_cpus()) {
372 mode_of_data = of_cpu_modes;
373 num_modes = ARRAY_SIZE(of_cpu_modes);
374 dev = &per_cpu(msm_cpu_spm_device, cpu);
375
376 } else {
377 mode_of_data = of_l2_modes;
378 num_modes = ARRAY_SIZE(of_l2_modes);
379 dev = &msm_spm_l2_device;
380 }
381
382 for (i = 0; i < num_modes; i++) {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600383 key = mode_of_data[i].key;
384 modes[mode_count].cmd =
385 (uint8_t *)of_get_property(node, key, &len);
386 if (!modes[mode_count].cmd)
387 continue;
388 modes[mode_count].mode = mode_of_data[i].id;
389 modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm;
390 mode_count++;
391 }
392
393 spm_data.modes = modes;
394 spm_data.num_modes = mode_count;
395
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600396 ret = msm_spm_dev_init(dev, &spm_data);
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600397
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600398 if (ret < 0)
399 pr_warn("%s():failed core-id:%u ret:%d\n", __func__, cpu, ret);
400
401 return ret;
402
403fail:
404 pr_err("%s: Failed reading node=%s, key=%s\n",
405 __func__, node->full_name, key);
406 return -EFAULT;
407}
408
Sathish Ambley86487e52012-06-11 13:46:11 -0700409static struct of_device_id msm_spm_match_table[] = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600410 {.compatible = "qcom,spm-v2"},
411 {},
412};
413
Sathish Ambley86487e52012-06-11 13:46:11 -0700414static struct platform_driver msm_spm_device_driver = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600415 .probe = msm_spm_dev_probe,
416 .driver = {
417 .name = "spm-v2",
418 .owner = THIS_MODULE,
419 .of_match_table = msm_spm_match_table,
420 },
421};
422
423int __init msm_spm_device_init(void)
424{
425 return platform_driver_register(&msm_spm_device_driver);
426}