blob: 9e9b6617c335d01d8086b17a2cb6266eaf590a78 [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);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043
44int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
45{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046 struct msm_spm_device *dev;
47 int ret = -EIO;
48
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049 dev = &per_cpu(msm_cpu_spm_device, cpu);
50 ret = msm_spm_drv_set_vdd(&dev->reg_data, vlevel);
51
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052 return ret;
53}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060054EXPORT_SYMBOL(msm_spm_set_vdd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070055
56static int msm_spm_dev_set_low_power_mode(struct msm_spm_device *dev,
57 unsigned int mode, bool notify_rpm)
58{
59 uint32_t i;
60 uint32_t start_addr = 0;
61 int ret = -EINVAL;
62
63 if (mode == MSM_SPM_MODE_DISABLED) {
64 ret = msm_spm_drv_set_spm_enable(&dev->reg_data, false);
65 } else if (!msm_spm_drv_set_spm_enable(&dev->reg_data, true)) {
66 for (i = 0; i < dev->num_modes; i++) {
67 if ((dev->modes[i].mode == mode) &&
68 (dev->modes[i].notify_rpm == notify_rpm)) {
69 start_addr = dev->modes[i].start_addr;
70 break;
71 }
72 }
73 ret = msm_spm_drv_set_low_power_mode(&dev->reg_data,
74 start_addr);
75 }
76 return ret;
77}
78
Stephen Boyddb354112012-05-09 14:24:58 -070079static int __devinit msm_spm_dev_init(struct msm_spm_device *dev,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080 struct msm_spm_platform_data *data)
81{
82 int i, ret = -ENOMEM;
83 uint32_t offset = 0;
84
85 dev->num_modes = data->num_modes;
86 dev->modes = kmalloc(
87 sizeof(struct msm_spm_power_modes) * dev->num_modes,
88 GFP_KERNEL);
89
90 if (!dev->modes)
91 goto spm_failed_malloc;
92
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060093 dev->reg_data.ver_reg = data->ver_reg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094 ret = msm_spm_drv_init(&dev->reg_data, data);
95
96 if (ret)
97 goto spm_failed_init;
98
99 for (i = 0; i < dev->num_modes; i++) {
100
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600101 /* Default offset is 0 and gets updated as we write more
102 * sequences into SPM
103 */
104 dev->modes[i].start_addr = offset;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105 ret = msm_spm_drv_write_seq_data(&dev->reg_data,
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600106 data->modes[i].cmd, &offset);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700107 if (ret < 0)
108 goto spm_failed_init;
109
110 dev->modes[i].mode = data->modes[i].mode;
111 dev->modes[i].notify_rpm = data->modes[i].notify_rpm;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112 }
113 msm_spm_drv_flush_seq_entry(&dev->reg_data);
114 return 0;
115
116spm_failed_init:
117 kfree(dev->modes);
118spm_failed_malloc:
119 return ret;
120}
121
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700122int msm_spm_turn_on_cpu_rail(unsigned int cpu)
123{
124 uint32_t val = 0;
125 uint32_t timeout = 0;
126 void *reg = NULL;
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800127 void *saw_bases[] = {
128 0,
129 MSM_SAW1_BASE,
130 MSM_SAW2_BASE,
131 MSM_SAW3_BASE
132 };
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700133
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800134 if (cpu == 0 || cpu >= num_possible_cpus())
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700135 return -EINVAL;
136
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800137 reg = saw_bases[cpu];
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700138
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700139 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
Stepan Moskovchenko9c749262012-07-09 19:30:44 -0700140 cpu_is_apq8064() || cpu_is_msm8627() || cpu_is_msm8960ab()) {
Stepan Moskovchenko2b0b06e2012-02-03 15:03:52 -0800141 val = 0xA4;
142 reg += 0x14;
143 timeout = 512;
Praveen Chidambaramc0750ca2012-01-08 10:03:28 -0700144 } else {
145 return -ENOSYS;
146 }
147
148 writel_relaxed(val, reg);
149 mb();
150 udelay(timeout);
151
152 return 0;
153}
154EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
155
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600156void msm_spm_reinit(void)
157{
158 unsigned int cpu;
159 for_each_possible_cpu(cpu)
160 msm_spm_drv_reinit(&per_cpu(msm_cpu_spm_device.reg_data, cpu));
161}
162EXPORT_SYMBOL(msm_spm_reinit);
163
164int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
165{
166 struct msm_spm_device *dev = &__get_cpu_var(msm_cpu_spm_device);
167 return msm_spm_dev_set_low_power_mode(dev, mode, notify_rpm);
168}
169EXPORT_SYMBOL(msm_spm_set_low_power_mode);
170
171/* Board file init function */
172int __init msm_spm_init(struct msm_spm_platform_data *data, int nr_devs)
173{
174 unsigned int cpu;
175 int ret = 0;
176
177 BUG_ON((nr_devs < num_possible_cpus()) || !data);
178
179 for_each_possible_cpu(cpu) {
180 struct msm_spm_device *dev = &per_cpu(msm_cpu_spm_device, cpu);
181 ret = msm_spm_dev_init(dev, &data[cpu]);
182 if (ret < 0) {
183 pr_warn("%s():failed CPU:%u ret:%d\n", __func__,
184 cpu, ret);
185 break;
186 }
187 }
188
189 return ret;
190}
191
192#ifdef CONFIG_MSM_L2_SPM
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193
194int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm)
195{
196 return msm_spm_dev_set_low_power_mode(
197 &msm_spm_l2_device, mode, notify_rpm);
198}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600199EXPORT_SYMBOL(msm_spm_l2_set_low_power_mode);
Maheshkumar Sivasubramanian4ac23762011-11-02 10:03:06 -0600200
201void msm_spm_l2_reinit(void)
202{
203 msm_spm_drv_reinit(&msm_spm_l2_device.reg_data);
204}
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600205EXPORT_SYMBOL(msm_spm_l2_reinit);
206
207int msm_spm_apcs_set_vdd(unsigned int vlevel)
208{
209 return msm_spm_drv_set_vdd(&msm_spm_l2_device.reg_data, vlevel);
210}
211EXPORT_SYMBOL(msm_spm_apcs_set_vdd);
212
213int msm_spm_apcs_set_phase(unsigned int phase_cnt)
214{
215 return msm_spm_drv_set_phase(&msm_spm_l2_device.reg_data, phase_cnt);
216}
217EXPORT_SYMBOL(msm_spm_apcs_set_phase);
218
219/* Board file init function */
220int __init msm_spm_l2_init(struct msm_spm_platform_data *data)
221{
222 return msm_spm_dev_init(&msm_spm_l2_device, data);
223}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224#endif
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600225
Sathish Ambley86487e52012-06-11 13:46:11 -0700226static int __devinit msm_spm_dev_probe(struct platform_device *pdev)
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600227{
228 int ret = 0;
229 int cpu = 0;
230 int i = 0;
231 struct device_node *node = pdev->dev.of_node;
232 struct msm_spm_platform_data spm_data;
233 char *key = NULL;
234 uint32_t val = 0;
235 struct msm_spm_seq_entry modes[MSM_SPM_MODE_NR];
236 size_t len = 0;
237 struct msm_spm_device *dev = NULL;
238 struct resource *res = NULL;
239 uint32_t mode_count = 0;
240
241 struct spm_of {
242 char *key;
243 uint32_t id;
244 };
245
246 struct spm_of spm_of_data[] = {
247 {"qcom,saw2-cfg", MSM_SPM_REG_SAW2_CFG},
248 {"qcom,saw2-avs-ctl", MSM_SPM_REG_SAW2_AVS_CTL},
249 {"qcom,saw2-avs-hysteresis", MSM_SPM_REG_SAW2_AVS_HYSTERESIS},
250 {"qcom,saw2-spm-ctl", MSM_SPM_REG_SAW2_SPM_CTL},
251 {"qcom,saw2-pmic-dly", MSM_SPM_REG_SAW2_PMIC_DLY},
252 {"qcom,saw2-avs-limit", MSM_SPM_REG_SAW2_AVS_LIMIT},
253 {"qcom,saw2-spm-dly", MSM_SPM_REG_SAW2_SPM_DLY},
254 {"qcom,saw2-pmic-data0", MSM_SPM_REG_SAW2_PMIC_DATA_0},
255 {"qcom,saw2-pmic-data1", MSM_SPM_REG_SAW2_PMIC_DATA_1},
256 {"qcom,saw2-pmic-data2", MSM_SPM_REG_SAW2_PMIC_DATA_2},
257 {"qcom,saw2-pmic-data3", MSM_SPM_REG_SAW2_PMIC_DATA_3},
258 {"qcom,saw2-pmic-data4", MSM_SPM_REG_SAW2_PMIC_DATA_4},
259 {"qcom,saw2-pmic-data5", MSM_SPM_REG_SAW2_PMIC_DATA_5},
260 {"qcom,saw2-pmic-data6", MSM_SPM_REG_SAW2_PMIC_DATA_6},
261 {"qcom,saw2-pmic-data7", MSM_SPM_REG_SAW2_PMIC_DATA_7},
262 };
263
264 struct mode_of {
265 char *key;
266 uint32_t id;
267 uint32_t notify_rpm;
268 };
269
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600270 struct mode_of of_cpu_modes[] = {
271 {"qcom,saw2-spm-cmd-wfi", MSM_SPM_MODE_CLOCK_GATING, 0},
272 {"qcom,saw2-spm-cmd-ret", MSM_SPM_MODE_POWER_RETENTION, 0},
273 {"qcom,saw2-spm-cmd-spc", MSM_SPM_MODE_POWER_COLLAPSE, 0},
274 {"qcom,saw2-spm-cmd-pc", MSM_SPM_MODE_POWER_COLLAPSE, 1},
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600275 };
276
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600277 struct mode_of of_l2_modes[] = {
278 {"qcom,saw2-spm-cmd-ret", MSM_SPM_L2_MODE_RETENTION, 1},
279 {"qcom,saw2-spm-cmd-gdhs", MSM_SPM_L2_MODE_GDHS, 1},
280 {"qcom,saw2-spm-cmd-pc", MSM_SPM_L2_MODE_POWER_COLLAPSE, 1},
281 };
282
283 struct mode_of *mode_of_data;
284 int num_modes;
285
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600286 memset(&spm_data, 0, sizeof(struct msm_spm_platform_data));
287 memset(&modes, 0,
288 (MSM_SPM_MODE_NR - 2) * sizeof(struct msm_spm_seq_entry));
289
290 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
291 if (!res)
292 goto fail;
293
294 spm_data.reg_base_addr = devm_ioremap(&pdev->dev, res->start,
295 resource_size(res));
296 if (!spm_data.reg_base_addr)
297 return -ENOMEM;
298
299 key = "qcom,core-id";
300 ret = of_property_read_u32(node, key, &val);
301 if (ret)
302 goto fail;
303 cpu = val;
304
305 key = "qcom,saw2-ver-reg";
306 ret = of_property_read_u32(node, key, &val);
307 if (ret)
308 goto fail;
309 spm_data.ver_reg = val;
310
311 key = "qcom,vctl-timeout-us";
312 ret = of_property_read_u32(node, key, &val);
313 if (!ret)
314 spm_data.vctl_timeout_us = val;
315
316 /* optional */
317 key = "qcom,vctl-port";
318 ret = of_property_read_u32(node, key, &val);
319 if (!ret)
320 spm_data.vctl_port = val;
321
322 /* optional */
323 key = "qcom,phase-port";
324 ret = of_property_read_u32(node, key, &val);
325 if (!ret)
326 spm_data.phase_port = val;
327
328 for (i = 0; i < ARRAY_SIZE(spm_of_data); i++) {
329 ret = of_property_read_u32(node, spm_of_data[i].key, &val);
330 if (ret)
331 continue;
332 spm_data.reg_init_values[spm_of_data[i].id] = val;
333 }
334
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600335 /*
336 * Device with id 0..NR_CPUS are SPM for apps cores
337 * Device with id 0xFFFF is for L2 SPM.
338 */
339 if (cpu >= 0 && cpu < num_possible_cpus()) {
340 mode_of_data = of_cpu_modes;
341 num_modes = ARRAY_SIZE(of_cpu_modes);
342 dev = &per_cpu(msm_cpu_spm_device, cpu);
343
344 } else {
345 mode_of_data = of_l2_modes;
346 num_modes = ARRAY_SIZE(of_l2_modes);
347 dev = &msm_spm_l2_device;
348 }
349
350 for (i = 0; i < num_modes; i++) {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600351 key = mode_of_data[i].key;
352 modes[mode_count].cmd =
353 (uint8_t *)of_get_property(node, key, &len);
354 if (!modes[mode_count].cmd)
355 continue;
356 modes[mode_count].mode = mode_of_data[i].id;
357 modes[mode_count].notify_rpm = mode_of_data[i].notify_rpm;
358 mode_count++;
359 }
360
361 spm_data.modes = modes;
362 spm_data.num_modes = mode_count;
363
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600364 ret = msm_spm_dev_init(dev, &spm_data);
Mahesh Sivasubramanian11373322012-06-14 11:17:20 -0600365
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600366 if (ret < 0)
367 pr_warn("%s():failed core-id:%u ret:%d\n", __func__, cpu, ret);
368
369 return ret;
370
371fail:
372 pr_err("%s: Failed reading node=%s, key=%s\n",
373 __func__, node->full_name, key);
374 return -EFAULT;
375}
376
Sathish Ambley86487e52012-06-11 13:46:11 -0700377static struct of_device_id msm_spm_match_table[] = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600378 {.compatible = "qcom,spm-v2"},
379 {},
380};
381
Sathish Ambley86487e52012-06-11 13:46:11 -0700382static struct platform_driver msm_spm_device_driver = {
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600383 .probe = msm_spm_dev_probe,
384 .driver = {
385 .name = "spm-v2",
386 .owner = THIS_MODULE,
387 .of_match_table = msm_spm_match_table,
388 },
389};
390
391int __init msm_spm_device_init(void)
392{
393 return platform_driver_register(&msm_spm_device_driver);
394}