blob: 6e81be6cb2e95315661983b01b04730992335f67 [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 Chidambaramaa9d52b2012-04-02 11:09:47 -060041static struct msm_spm_device msm_spm_l2_device;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_spm_device, msm_cpu_spm_device);
43static atomic_t msm_spm_set_vdd_x_cpu_allowed = ATOMIC_INIT(1);
44
45void msm_spm_allow_x_cpu_set_vdd(bool allowed)
46{
47 atomic_set(&msm_spm_set_vdd_x_cpu_allowed, allowed ? 1 : 0);
48}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060049EXPORT_SYMBOL(msm_spm_allow_x_cpu_set_vdd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050
51int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
52{
53 unsigned long flags;
54 struct msm_spm_device *dev;
55 int ret = -EIO;
56
57 local_irq_save(flags);
58 if (!atomic_read(&msm_spm_set_vdd_x_cpu_allowed) &&
59 unlikely(smp_processor_id() != cpu)) {
60 goto set_vdd_x_cpu_bail;
61 }
62
63 dev = &per_cpu(msm_cpu_spm_device, cpu);
64 ret = msm_spm_drv_set_vdd(&dev->reg_data, vlevel);
65
66set_vdd_x_cpu_bail:
67 local_irq_restore(flags);
68 return ret;
69}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060070EXPORT_SYMBOL(msm_spm_set_vdd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071
72static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
73 unsigned int mode, bool notify_rpm)
74{
75 uint32_t i;
76 uint32_t start_addr = 0;
77 int ret = -EINVAL;
78
79 if (mode == MSM_SPM_MODE_DISABLED) {
80 ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
81 } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
82 for (i = 0; i < dev->num_modes; i++) {
83 if ((dev->modes[i].mode == mode) &&
84 (dev->modes[i].notify_rpm == notify_rpm)) {
85 start_addr = dev->modes[i].start_addr;
86 break;
87 }
88 }
89 ret = msm_spm_drv_set_low_power_mode(&dev->reg_data,
90 start_addr);
91 }
92 return ret;
93}
94
Stephen Boyddb354112012-05-09 14:24:58 -070095static int __devinit msm_spm_dev_init(struct msm_spm_device *dev,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096 struct msm_spm_platform_data *data)
97{
98 int i, ret = -ENOMEM;
99 uint32_t offset = 0;
100
101 dev->num_modes = data->num_modes;
102 dev->modes = kmalloc(
103 sizeof(struct msm_spm_power_modes) * dev->num_modes,
104 GFP_KERNEL);
105
106 if (!dev->modes)
107 goto spm_failed_malloc;
108
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600109 dev->reg_data.ver_reg = data->ver_reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110 ret = msm_spm_drv_init(&dev->reg_data, data);
111
112 if (ret)
113 goto spm_failed_init;
114
115 for (i = 0; i < dev->num_modes; i++) {
116
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600117 /* Default offset is 0 and gets updated as we write more
118 * sequences into SPM
119 */
120 dev->modes[i].start_addr = offset;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121 ret = msm_spm_drv_write_seq_data(&dev->reg_data,
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600122 data->modes[i].cmd, &offset);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123 if (ret < 0)
124 goto spm_failed_init;
125
126 dev->modes[i].mode = data->modes[i].mode;
127 dev->modes[i].notify_rpm = data->modes[i].notify_rpm;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700128 }
129 msm_spm_drv_flush_seq_entry(&dev->reg_data);
130 return 0;
131
132spm_failed_init:
133 kfree(dev->modes);
134spm_failed_malloc:
135 return ret;
136}
137
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700138int msm_spm_turn_on_cpu_rail(unsigned int cpu)
139{
140 uint32_t val = 0;
141 uint32_t timeout = 0;
142 void *reg = NULL;
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800143 void *saw_bases[] = {
144 0,
145 MSM_SAW1_BASE,
146 MSM_SAW2_BASE,
147 MSM_SAW3_BASE
148 };
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700149
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800150 if (cpu == 0 || cpu >= num_possible_cpus())
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700151 return -EINVAL;
152
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800153 reg = saw_bases[cpu];
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700154
Mahesh Sivasubramaniancd91c8f2012-03-07 16:59:05 -0700155 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_apq8064()) {
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800156 val = 0xA4;
157 reg += 0x14;
158 timeout = 512;
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700159 } else {
160 return -ENOSYS;
161 }
162
163 writel_relaxed(val, reg);
164 mb();
165 udelay(timeout);
166
167 return 0;
168}
169EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
170
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600171void msm_spm_reinit(void)
172{
173 unsigned int cpu;
174 for_each_possible_cpu(cpu)
175 msm_spm_drv_reinit(&per_cpu(msm_cpu_spm_device.reg_data, cpu));
176}
177EXPORT_SYMBOL(msm_spm_reinit);
178
179int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
180{
181 struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
182 return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
183}
184EXPORT_SYMBOL(msm_spm_set_low_power_mode);
185
186/* Board file init function */
187int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
188{
189 unsigned int cpu;
190 int ret = 0;
191
192 BUG_ON((nr_devs < num_possible_cpus()) || !data);
193
194 for_each_possible_cpu(cpu) {
195 struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu);
196 ret = msm_spm_dev_init(dev, &data[cpu]);
197 if (ret < 0) {
198 pr_warn("%s():failed CPU:%u ret:%d\n", __func__,
199 cpu, ret);
200 break;
201 }
202 }
203
204 return ret;
205}
206
207#ifdef CONFIG_MSM_L2_SPM
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208
209int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm)
210{
211 return msm_spm_dev_set_low_power_mode(
212 &msm_spm_l2_device, mode, notify_rpm);
213}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600214EXPORT_SYMBOL(msm_spm_l2_set_low_power_mode);
Maheshkumar Sivasubramanian4ac23762011-11-02 10:03:06 -0600215
216void msm_spm_l2_reinit(void)
217{
218 msm_spm_drv_reinit(&msm_spm_l2_device.reg_data);
219}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600220EXPORT_SYMBOL(msm_spm_l2_reinit);
221
222int msm_spm_apcs_set_vdd(unsigned int vlevel)
223{
224 return msm_spm_drv_set_vdd(&msm_spm_l2_device.reg_data, vlevel);
225}
226EXPORT_SYMBOL(msm_spm_apcs_set_vdd);
227
228int msm_spm_apcs_set_phase(unsigned int phase_cnt)
229{
230 return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt);
231}
232EXPORT_SYMBOL(msm_spm_apcs_set_phase);
233
234/* Board file init function */
235int __init msm_spm_l2_init(struct msm_spm_platform_data *data)
236{
237 return msm_spm_dev_init(&msm_spm_l2_device, data);
238}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239#endif
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600240
Sathish Ambley86487e52012-06-11 13:46:11 -0700241static int __devinit msm_spm_dev_probe(struct platform_device *pdev)
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600242{
243 int ret = 0;
244 int cpu = 0;
245 int i = 0;
246 struct device_node *node = pdev->dev.of_node;
247 struct msm_spm_platform_data spm_data;
248 char *key = NULL;
249 uint32_t val = 0;
250 struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR];
251 size_t len = 0;
252 struct msm_spm_device *dev = NULL;
253 struct resource *res = NULL;
254 uint32_t mode_count = 0;
255
256 struct spm_of {
257 char *key;
258 uint32_t id;
259 };
260
261 struct spm_of spm_of_data[] = {
262 {"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG},
263 {"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW2_AVS_CTL},
264 {"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW2_AVS_HYSTERESIS},
265 {"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL},
266 {"qcom,saw2-pmic-dly", MSM_SPM_REG_SAW2_PMIC_DLY},
267 {"qcom,saw2-avs-limit", MSM_SPM_REG_SAW2_AVS_LIMIT},
268 {"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY},
269 {"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0},
270 {"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1},
271 {"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2},
272 {"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3},
273 {"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4},
274 {"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5},
275 {"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6},
276 {"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7},
277 };
278
279 struct mode_of {
280 char *key;
281 uint32_t id;
282 uint32_t notify_rpm;
283 };
284
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600285 struct mode_of of_cpu_modes[] = {
286 {"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0},
287 {"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_POWER_RETENTION, 0},
288 {"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0},
289 {"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600290 };
291
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600292 struct mode_of of_l2_modes[] = {
293 {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 1},
294 {"qcom,saw2-spm-cmd-gdhs", MSM_SPM_L2_MODE_GDHS, 1},
295 {"qcom,saw2-spm-cmd-pc", MSM_SPM_L2_MODE_POWER_COLLAPSE, 1},
296 };
297
298 struct mode_of *mode_of_data;
299 int num_modes;
300
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600301 memset(&spm_data, 0, sizeof(struct msm_spm_platform_data));
302 memset(&modes, 0,
303 (MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
304
305 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
306 if (!res)
307 goto fail;
308
309 spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
310 resource_size(res));
311 if (!spm_data.reg_base_addr)
312 return -ENOMEM;
313
314 key = "qcom,core-id";
315 ret = of_property_read_u32(node, key, &val);
316 if (ret)
317 goto fail;
318 cpu = val;
319
320 key = "qcom,saw2-ver-reg";
321 ret = of_property_read_u32(node, key, &val);
322 if (ret)
323 goto fail;
324 spm_data.ver_reg = val;
325
326 key = "qcom,vctl-timeout-us";
327 ret = of_property_read_u32(node, key, &val);
328 if (!ret)
329 spm_data.vctl_timeout_us = val;
330
331 /* optional */
332 key = "qcom,vctl-port";
333 ret = of_property_read_u32(node, key, &val);
334 if (!ret)
335 spm_data.vctl_port = val;
336
337 /* optional */
338 key = "qcom,phase-port";
339 ret = of_property_read_u32(node, key, &val);
340 if (!ret)
341 spm_data.phase_port = val;
342
343 for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
344 ret = of_property_read_u32(node, spm_of_data[i].key, &val);
345 if (ret)
346 continue;
347 spm_data.reg_init_values[spm_of_data[i].id] = val;
348 }
349
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600350 /*
351 * Device with id 0..NR_CPUS are SPM for apps cores
352 * Device with id 0xFFFF is for L2 SPM.
353 */
354 if (cpu >= 0 && cpu < num_possible_cpus()) {
355 mode_of_data = of_cpu_modes;
356 num_modes = ARRAY_SIZE(of_cpu_modes);
357 dev = &per_cpu(msm_cpu_spm_device, cpu);
358
359 } else {
360 mode_of_data = of_l2_modes;
361 num_modes = ARRAY_SIZE(of_l2_modes);
362 dev = &msm_spm_l2_device;
363 }
364
365 for (i = 0; i < num_modes; i++) {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600366 key = mode_of_data[i].key;
367 modes[mode_count].cmd =
368 (uint8_t *)of_get_property(node, key, &len);
369 if (!modes[mode_count].cmd)
370 continue;
371 modes[mode_count].mode = mode_of_data[i].id;
372 modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm;
373 mode_count++;
374 }
375
376 spm_data.modes = modes;
377 spm_data.num_modes = mode_count;
378
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600379 ret = msm_spm_dev_init(dev, &spm_data);
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600380
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600381 if (ret < 0)
382 pr_warn("%s():failed core-id:%u ret:%d\n", __func__, cpu, ret);
383
384 return ret;
385
386fail:
387 pr_err("%s: Failed reading node=%s, key=%s\n",
388 __func__, node->full_name, key);
389 return -EFAULT;
390}
391
Sathish Ambley86487e52012-06-11 13:46:11 -0700392static struct of_device_id msm_spm_match_table[] = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600393 {.compatible = "qcom,spm-v2"},
394 {},
395};
396
Sathish Ambley86487e52012-06-11 13:46:11 -0700397static struct platform_driver msm_spm_device_driver = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600398 .probe = msm_spm_dev_probe,
399 .driver = {
400 .name = "spm-v2",
401 .owner = THIS_MODULE,
402 .of_match_table = msm_spm_match_table,
403 },
404};
405
406int __init msm_spm_device_init(void)
407{
408 return platform_driver_register(&msm_spm_device_driver);
409}