blob: 620aa1d1f810b3a9c33875095c374fa9f4e412f1 [file] [log] [blame]
Maheshkumar Sivasubramanian4ac23762011-11-02 10:03:06 -06001/* 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>
20#include <mach/msm_iomap.h>
21
22#include "spm_driver.h"
23
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060024#define MSM_SPM_PMIC_STATE_IDLE 0
25
26#define SAW2_V1_VER_REG 0x04
27#define SAW2_V2_VER_REG 0xfd0
28
29#define SAW2_MAJOR_2 2
30
31
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032enum {
33 MSM_SPM_DEBUG_SHADOW = 1U << 0,
34 MSM_SPM_DEBUG_VCTL = 1U << 1,
35};
36
37static int msm_spm_debug_mask;
38module_param_named(
39 debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
40);
41
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060043static uint32_t msm_spm_reg_offsets_v1[MSM_SPM_REG_NR] = {
44 [MSM_SPM_REG_SAW2_SECURE] = 0x00,
45 [MSM_SPM_REG_SAW2_ID] = 0x04,
46 [MSM_SPM_REG_SAW2_CFG] = 0x08,
47 [MSM_SPM_REG_SAW2_STS0] = 0x0C,
48 [MSM_SPM_REG_SAW2_STS1] = 0x10,
49 [MSM_SPM_REG_SAW2_VCTL] = 0x14,
50 [MSM_SPM_REG_SAW2_AVS_CTL] = 0x18,
51 [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x1C,
52 [MSM_SPM_REG_SAW2_SPM_CTL] = 0x20,
53 [MSM_SPM_REG_SAW2_PMIC_DLY] = 0x24,
54 [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x28,
55 [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x2C,
56 [MSM_SPM_REG_SAW2_RST] = 0x30,
57 [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80,
58};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060060static uint32_t msm_spm_reg_offsets_v2[MSM_SPM_REG_NR] = {
61 [MSM_SPM_REG_SAW2_SECURE] = 0x00,
62 [MSM_SPM_REG_SAW2_ID] = 0x04,
63 [MSM_SPM_REG_SAW2_CFG] = 0x08,
64 [MSM_SPM_REG_SAW2_SPM_STS] = 0x0C,
65 [MSM_SPM_REG_SAW2_AVS_STS] = 0x10,
66 [MSM_SPM_REG_SAW2_PMIC_STS] = 0x14,
67 [MSM_SPM_REG_SAW2_RST] = 0x18,
68 [MSM_SPM_REG_SAW2_VCTL] = 0x1C,
69 [MSM_SPM_REG_SAW2_AVS_CTL] = 0x20,
70 [MSM_SPM_REG_SAW2_AVS_LIMIT] = 0x24,
71 [MSM_SPM_REG_SAW2_AVS_DLY] = 0x28,
72 [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x2C,
73 [MSM_SPM_REG_SAW2_SPM_CTL] = 0x30,
74 [MSM_SPM_REG_SAW2_SPM_DLY] = 0x34,
75 [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x40,
76 [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x44,
77 [MSM_SPM_REG_SAW2_PMIC_DATA_2] = 0x48,
78 [MSM_SPM_REG_SAW2_PMIC_DATA_3] = 0x4C,
79 [MSM_SPM_REG_SAW2_PMIC_DATA_4] = 0x50,
80 [MSM_SPM_REG_SAW2_PMIC_DATA_5] = 0x54,
81 [MSM_SPM_REG_SAW2_PMIC_DATA_6] = 0x58,
82 [MSM_SPM_REG_SAW2_PMIC_DATA_7] = 0x5C,
83 [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80,
84 [MSM_SPM_REG_SAW2_VERSION] = 0xFD0,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085};
86
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -060087static inline uint32_t msm_spm_drv_get_num_spm_entry(
88 struct msm_spm_driver_data *dev)
89{
90 return 32;
91}
92
93static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev,
94 unsigned int reg_index)
95{
96 __raw_writel(dev->reg_shadow[reg_index],
97 dev->reg_base_addr + dev->reg_offsets[reg_index]);
98}
99
100static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev,
101 unsigned int reg_index)
102{
103 dev->reg_shadow[reg_index] =
104 __raw_readl(dev->reg_base_addr +
105 dev->reg_offsets[reg_index]);
106}
107
108static inline void msm_spm_drv_set_start_addr(
109 struct msm_spm_driver_data *dev, uint32_t addr)
110{
111 addr &= 0x7F;
112 addr <<= 4;
113 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F;
114 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr;
115}
116
117static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev)
118{
119 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_ID);
120
121 if (dev->major == SAW2_MAJOR_2)
122 return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 2) & 0x1;
123 else
124 return (dev->reg_shadow[MSM_SPM_REG_SAW2_ID] >> 18) & 0x1;
125}
126
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127static inline void msm_spm_drv_set_vctl(struct msm_spm_driver_data *dev,
128 uint32_t vlevel)
129{
130 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0xFF;
131 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= vlevel;
132
133 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] &= ~0xFF;
134 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] |= vlevel;
Praveen Chidambaramf3f2b3e2012-03-21 20:13:38 -0600135
136 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] &= ~0x3F;
137 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] |= (vlevel & 0x3F);
Girish Mahadevand27ca4a2012-08-15 09:21:23 -0600138
139 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] &= ~0x3F0000;
140 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] |=
141 ((vlevel & 0x3F) << 16);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142}
143
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600144static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
145 uint32_t vlevel)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146{
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600147 unsigned int pmic_data = 0;
148
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600149 /**
150 * VCTL_PORT has to be 0, for PMIC_STS register to be updated.
151 * Ensure that vctl_port is always set to 0.
152 */
153 WARN_ON(dev->vctl_port);
154
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600155 pmic_data |= vlevel;
156 pmic_data |= (dev->vctl_port & 0x7) << 16;
157
158 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
159 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160}
161
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600162static inline void msm_spm_drv_apcs_set_vctl(struct msm_spm_driver_data *dev,
163 unsigned int vlevel)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164{
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600165 if (dev->major == SAW2_MAJOR_2)
166 return msm_spm_drv_set_vctl2(dev, vlevel);
167 else
168 return msm_spm_drv_set_vctl(dev, vlevel);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169}
170
171static inline uint32_t msm_spm_drv_get_sts_pmic_state(
172 struct msm_spm_driver_data *dev)
173{
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600174 if (dev->major == SAW2_MAJOR_2) {
175 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
176 return (dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] >> 16) &
177 0x03;
178 } else {
179 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS0);
180 return (dev->reg_shadow[MSM_SPM_REG_SAW2_STS0] >> 10) & 0x03;
181 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182}
183
Praveen Chidambaram4133ba12012-09-29 22:27:03 -0600184uint32_t msm_spm_drv_get_sts_curr_pmic_data(
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185 struct msm_spm_driver_data *dev)
186{
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600187 if (dev->major == SAW2_MAJOR_2) {
188 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_PMIC_STS);
189 return dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_STS] & 0xFF;
190 } else {
191 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS1);
192 return dev->reg_shadow[MSM_SPM_REG_SAW2_STS1] & 0xFF;
193 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194}
195
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600196static inline uint32_t msm_spm_drv_get_saw2_ver(struct msm_spm_driver_data *dev,
197 uint32_t *major, uint32_t *minor)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198{
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600199 int ret = -ENODEV;
200 uint32_t val = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600202 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_VERSION);
203 val = dev->reg_shadow[MSM_SPM_REG_SAW2_VERSION];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600205 if (dev->ver_reg == SAW2_V2_VER_REG) {
206 *major = (val >> 28) & 0xF;
207 *minor = (val >> 16) & 0xFFF;
208 ret = 0;
209 } else if (dev->ver_reg == SAW2_V1_VER_REG) {
210 *major = (val >> 4) & 0xF;
211 *minor = val & 0xF;
212 ret = 0;
213 }
214
215 return ret;
216}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218inline int msm_spm_drv_set_spm_enable(
219 struct msm_spm_driver_data *dev, bool enable)
220{
221 uint32_t value = enable ? 0x01 : 0x00;
222
223 if (!dev)
224 return -EINVAL;
225
226 if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) {
227
228 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1;
229 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value;
230
231 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
232 wmb();
233 }
234 return 0;
235}
236void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev)
237{
238 int i;
239 int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
240
241 if (!dev) {
242 __WARN();
243 return;
244 }
245
246 for (i = 0; i < num_spm_entry; i++) {
247 __raw_writel(dev->reg_seq_entry_shadow[i],
248 dev->reg_base_addr
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600249 + dev->reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250 + 4 * i);
251 }
252 mb();
253}
254
255int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600256 uint8_t *cmd, uint32_t *offset)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257{
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600258 uint32_t cmd_w;
259 uint32_t offset_w = *offset / 4;
260 uint8_t last_cmd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600262 if (!cmd)
263 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264
265 while (1) {
266 int i;
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600267 cmd_w = 0;
268 last_cmd = 0;
269 cmd_w = dev->reg_seq_entry_shadow[offset_w];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600271 for (i = (*offset % 4) ; i < 4; i++) {
272 last_cmd = *(cmd++);
273 cmd_w |= last_cmd << (i * 8);
274 (*offset)++;
275 if (last_cmd == 0x0f)
276 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277 }
278
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 dev->reg_seq_entry_shadow[offset_w++] = cmd_w;
280 if (last_cmd == 0x0f)
281 break;
282 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600284 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285}
286
287int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
288 uint32_t addr)
289{
290
291 /* SPM is configured to reset start address to zero after end of Program
292 */
293 if (!dev)
294 return -EINVAL;
295
296 msm_spm_drv_set_start_addr(dev, addr);
297
Maheshkumar Sivasubramaniandaa23bb2011-07-01 17:24:51 -0600298 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
299 wmb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300
301 if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) {
302 int i;
303 for (i = 0; i < MSM_SPM_REG_NR; i++)
304 pr_info("%s: reg %02x = 0x%08x\n", __func__,
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600305 dev->reg_offsets[i], dev->reg_shadow[i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700306 }
307
308 return 0;
309}
310
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600311#ifdef CONFIG_MSM_AVS_HW
Mahesh Sivasubramanian1974a362012-10-19 09:37:15 -0600312static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
313{
314 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
315 if (dev->major == SAW2_MAJOR_2)
316 return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(0);
317 else
318 return dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] & BIT(27);
319}
320
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600321static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
322{
323 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
324 dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~BIT(27);
325 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
326}
327
328static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev)
329{
330 dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= BIT(27);
331 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
332}
333
334static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
335 unsigned int vlevel)
336{
337 vlevel &= 0x3f;
338 dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] &= ~0x7efc00;
339 dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= ((vlevel - 4) << 10);
340 dev->reg_shadow[MSM_SPM_REG_SAW2_AVS_CTL] |= (vlevel << 17);
341 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_AVS_CTL);
342}
343
344#else
Mahesh Sivasubramanian1974a362012-10-19 09:37:15 -0600345static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
346{
347 return false;
348}
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600349
350static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
351
352static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev) { }
353
354static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
355 unsigned int vlevel) { }
356#endif
357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
359{
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600360 uint32_t timeout_us, new_level;
Mahesh Sivasubramanian1974a362012-10-19 09:37:15 -0600361 bool avs_enabled = msm_spm_drv_is_avs_enabled(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362
363 if (!dev)
364 return -EINVAL;
365
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600366 if (!msm_spm_pmic_arb_present(dev))
367 return -ENOSYS;
368
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600370 pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
371
Mahesh Sivasubramanian1974a362012-10-19 09:37:15 -0600372 if (avs_enabled)
373 msm_spm_drv_disable_avs(dev);
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600374
375 /* Kick the state machine back to idle */
376 dev->reg_shadow[MSM_SPM_REG_SAW2_RST] = 1;
377 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_RST);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600379 msm_spm_drv_apcs_set_vctl(dev, vlevel);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
381 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_0);
Praveen Chidambaramf3f2b3e2012-03-21 20:13:38 -0600382 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 timeout_us = dev->vctl_timeout_us;
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600385 /* Confirm the voltage we set was what hardware sent */
386 do {
387 new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);
388 if (new_level == vlevel)
389 break;
390 udelay(1);
391 } while (--timeout_us);
392 if (!timeout_us) {
393 pr_info("Wrong level %#x\n", new_level);
394 goto set_vdd_bail;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395 }
396
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397 if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600398 pr_info("%s: done, remaining timeout %u us\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399 __func__, timeout_us);
400
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600401 /* Set AVS min/max */
Mahesh Sivasubramanian1974a362012-10-19 09:37:15 -0600402 if (avs_enabled) {
403 msm_spm_drv_set_avs_vlevel(dev, vlevel);
404 msm_spm_drv_enable_avs(dev);
405 }
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600406
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 return 0;
408
409set_vdd_bail:
Mahesh Sivasubramanian1974a362012-10-19 09:37:15 -0600410 if (avs_enabled)
411 msm_spm_drv_enable_avs(dev);
412
413 pr_err("%s: failed %#x, remaining timeout %uus, vlevel %#x\n",
Praveen Chidambaram631f2822012-03-16 12:08:42 -0600414 __func__, vlevel, timeout_us, new_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415 return -EIO;
416}
417
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600418static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
419 enum msm_spm_pmic_port port)
420{
421 int index = -1;
422
423 switch (port) {
424 case MSM_SPM_PMIC_VCTL_PORT:
425 index = dev->vctl_port;
426 break;
427 case MSM_SPM_PMIC_PHASE_PORT:
428 index = dev->phase_port;
429 break;
430 case MSM_SPM_PMIC_PFM_PORT:
431 index = dev->pfm_port;
432 break;
433 default:
434 break;
435 }
436
437 return index;
438}
439
440int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
441 enum msm_spm_pmic_port port, unsigned int data)
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600442{
443 unsigned int pmic_data = 0;
444 unsigned int timeout_us = 0;
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600445 int index = 0;
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600446
447 if (dev->major != SAW2_MAJOR_2)
448 return -ENODEV;
449
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600450 if (!msm_spm_pmic_arb_present(dev))
451 return -ENOSYS;
452
453 index = msm_spm_drv_get_pmic_port(dev, port);
454 if (index < 0)
455 return -ENODEV;
456
457 pmic_data |= data & 0xFF;
458 pmic_data |= (index & 0x7) << 16;
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600459
460 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0x700FF;
461 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= pmic_data;
462 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
463 mb();
464
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600465 timeout_us = dev->vctl_timeout_us;
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600466 /**
467 * Confirm the pmic data set was what hardware sent by
468 * checking the PMIC FSM state.
469 * We cannot use the sts_pmic_data and check it against
470 * the value like we do fot set_vdd, since the PMIC_STS
471 * is only updated for SAW_VCTL sent with port index 0.
472 */
473 do {
474 if (msm_spm_drv_get_sts_pmic_state(dev) ==
475 MSM_SPM_PMIC_STATE_IDLE)
476 break;
477 udelay(1);
478 } while (--timeout_us);
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600479
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600480 if (!timeout_us) {
481 pr_err("%s: failed, remaining timeout %u us, data %d\n",
482 __func__, timeout_us, data);
483 return -EIO;
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600484 }
485
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600486 return 0;
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600487}
488
Maheshkumar Sivasubramanian4ac23762011-11-02 10:03:06 -0600489void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
490{
491 int i;
492
493 for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
494 msm_spm_drv_flush_shadow(dev, i);
495
496 msm_spm_drv_flush_seq_entry(dev);
497 mb();
498}
499
Stephen Boyddb354112012-05-09 14:24:58 -0700500int __devinit msm_spm_drv_init(struct msm_spm_driver_data *dev,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700501 struct msm_spm_platform_data *data)
502{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503 int i;
504 int num_spm_entry;
505
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700506 BUG_ON(!dev || !data);
507
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600508 if (dev->ver_reg == SAW2_V2_VER_REG)
509 dev->reg_offsets = msm_spm_reg_offsets_v2;
510 else
511 dev->reg_offsets = msm_spm_reg_offsets_v1;
512
513 dev->vctl_port = data->vctl_port;
514 dev->phase_port = data->phase_port;
Praveen Chidambaram1dbe4952012-10-03 17:06:02 -0600515 dev->pfm_port = data->pfm_port;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700516 dev->reg_base_addr = data->reg_base_addr;
517 memcpy(dev->reg_shadow, data->reg_init_values,
518 sizeof(data->reg_init_values));
519
520 dev->vctl_timeout_us = data->vctl_timeout_us;
521
522 for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
523 msm_spm_drv_flush_shadow(dev, i);
524 /* barrier to ensure write completes before we update shadow
525 * registers
526 */
527 mb();
528
529 for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
530 msm_spm_drv_load_shadow(dev, i);
531
532 /* barrier to ensure read completes before we proceed further*/
533 mb();
534
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600535 msm_spm_drv_get_saw2_ver(dev, &dev->major, &dev->minor);
536
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
538
539 dev->reg_seq_entry_shadow =
Praveen Chidambaramaa9d52b2012-04-02 11:09:47 -0600540 kzalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 GFP_KERNEL);
542
543 if (!dev->reg_seq_entry_shadow)
544 return -ENOMEM;
545
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700546 return 0;
547}