blob: 2a6294f1f0139f34ef3783399f7f24b549159649 [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
24enum {
25 MSM_SPM_DEBUG_SHADOW = 1U << 0,
26 MSM_SPM_DEBUG_VCTL = 1U << 1,
27};
28
29static int msm_spm_debug_mask;
30module_param_named(
31 debug_mask, msm_spm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
32);
33
34#define MSM_SPM_PMIC_STATE_IDLE 0
35
36
37static uint32_t msm_spm_reg_offsets[MSM_SPM_REG_NR] = {
38 [MSM_SPM_REG_SAW2_SECURE] = 0x00,
39
40 [MSM_SPM_REG_SAW2_ID] = 0x04,
41 [MSM_SPM_REG_SAW2_CFG] = 0x08,
42 [MSM_SPM_REG_SAW2_STS0] = 0x0C,
43 [MSM_SPM_REG_SAW2_STS1] = 0x10,
44
45 [MSM_SPM_REG_SAW2_VCTL] = 0x14,
46
47 [MSM_SPM_REG_SAW2_AVS_CTL] = 0x18,
48 [MSM_SPM_REG_SAW2_AVS_HYSTERESIS] = 0x1C,
49
50 [MSM_SPM_REG_SAW2_SPM_CTL] = 0x20,
51 [MSM_SPM_REG_SAW2_PMIC_DLY] = 0x24,
52 [MSM_SPM_REG_SAW2_PMIC_DATA_0] = 0x28,
53 [MSM_SPM_REG_SAW2_PMIC_DATA_1] = 0x2C,
54 [MSM_SPM_REG_SAW2_RST] = 0x30,
55
56 [MSM_SPM_REG_SAW2_SEQ_ENTRY] = 0x80,
57};
58
59/******************************************************************************
60 * Internal helper functions
61 *****************************************************************************/
62
63static inline void msm_spm_drv_set_vctl(struct msm_spm_driver_data *dev,
64 uint32_t vlevel)
65{
66 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] &= ~0xFF;
67 dev->reg_shadow[MSM_SPM_REG_SAW2_VCTL] |= vlevel;
68
69 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] &= ~0xFF;
70 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] |= vlevel;
Praveen Chidambaramf3f2b3e2012-03-21 20:13:38 -060071
72 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] &= ~0x3F;
73 dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_1] |= (vlevel & 0x3F);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074}
75
76static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev,
77 unsigned int reg_index)
78{
79 __raw_writel(dev->reg_shadow[reg_index],
80 dev->reg_base_addr + msm_spm_reg_offsets[reg_index]);
81}
82
83static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev,
84 unsigned int reg_index)
85{
86 dev->reg_shadow[reg_index] =
87 __raw_readl(dev->reg_base_addr +
88 msm_spm_reg_offsets[reg_index]);
89}
90
91static inline uint32_t msm_spm_drv_get_awake_vlevel(
92 struct msm_spm_driver_data *dev)
93{
94 return dev->reg_shadow[MSM_SPM_REG_SAW2_PMIC_DATA_0] & 0xFF;
95}
96
97static inline uint32_t msm_spm_drv_get_sts_pmic_state(
98 struct msm_spm_driver_data *dev)
99{
100 return (dev->reg_shadow[MSM_SPM_REG_SAW2_STS0] >> 10) & 0x03;
101}
102
103static inline uint32_t msm_spm_drv_get_sts_curr_pmic_data(
104 struct msm_spm_driver_data *dev)
105{
106 return dev->reg_shadow[MSM_SPM_REG_SAW2_STS1] & 0xFF;
107}
108
109static inline uint32_t msm_spm_drv_get_num_spm_entry(
110 struct msm_spm_driver_data *dev)
111{
112 return 32;
113}
114
115static inline void msm_spm_drv_set_start_addr(
116 struct msm_spm_driver_data *dev, uint32_t addr)
117{
118 addr &= 0x7F;
119 addr <<= 4;
120 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= 0xFFFFF80F;
121 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= addr;
122}
123
124
125/******************************************************************************
126 * Public functions
127 *****************************************************************************/
128inline int msm_spm_drv_set_spm_enable(
129 struct msm_spm_driver_data *dev, bool enable)
130{
131 uint32_t value = enable ? 0x01 : 0x00;
132
133 if (!dev)
134 return -EINVAL;
135
136 if ((dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] & 0x01) ^ value) {
137
138 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] &= ~0x1;
139 dev->reg_shadow[MSM_SPM_REG_SAW2_SPM_CTL] |= value;
140
141 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
142 wmb();
143 }
144 return 0;
145}
146void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev)
147{
148 int i;
149 int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
150
151 if (!dev) {
152 __WARN();
153 return;
154 }
155
156 for (i = 0; i < num_spm_entry; i++) {
157 __raw_writel(dev->reg_seq_entry_shadow[i],
158 dev->reg_base_addr
159 + msm_spm_reg_offsets[MSM_SPM_REG_SAW2_SEQ_ENTRY]
160 + 4 * i);
161 }
162 mb();
163}
164
165int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
166 uint8_t *cmd, uint32_t offset)
167{
168 uint32_t offset_w = offset / 4;
169 int ret = 0;
170
171 if (!cmd || !dev) {
172 __WARN();
173 goto failed_write_seq_data;
174 };
175
176 while (1) {
177 int i;
178 uint32_t cmd_w = 0;
179 uint8_t last_cmd = 0;
180
181 for (i = 0; i < 4; i++) {
182 last_cmd = (last_cmd == 0x0f) ? 0x0f : *(cmd + i);
183 cmd_w |= last_cmd << (i * 8);
184 ret++;
185 }
186
187 if (offset_w >= msm_spm_drv_get_num_spm_entry(dev)) {
188 __WARN();
189 goto failed_write_seq_data;
190 }
191
192 cmd += i;
193 dev->reg_seq_entry_shadow[offset_w++] = cmd_w;
194 if (last_cmd == 0x0f)
195 break;
196 }
197 return ret;
198
199failed_write_seq_data:
200 return -EINVAL;
201}
202
203int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
204 uint32_t addr)
205{
206
207 /* SPM is configured to reset start address to zero after end of Program
208 */
209 if (!dev)
210 return -EINVAL;
211
212 msm_spm_drv_set_start_addr(dev, addr);
213
Maheshkumar Sivasubramaniandaa23bb2011-07-01 17:24:51 -0600214 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_SPM_CTL);
215 wmb();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216
217 if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) {
218 int i;
219 for (i = 0; i < MSM_SPM_REG_NR; i++)
220 pr_info("%s: reg %02x = 0x%08x\n", __func__,
221 msm_spm_reg_offsets[i], dev->reg_shadow[i]);
222 }
223
224 return 0;
225}
226
227int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
228{
229 uint32_t timeout_us;
230
231 if (!dev)
232 return -EINVAL;
233
234 if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
235 pr_info("%s: requesting vlevel 0x%x\n",
236 __func__, vlevel);
237
238 msm_spm_drv_set_vctl(dev, vlevel);
239 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_VCTL);
240 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_0);
Praveen Chidambaramf3f2b3e2012-03-21 20:13:38 -0600241 msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW2_PMIC_DATA_1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242 mb();
243
244 /* Wait for PMIC state to return to idle or until timeout */
245 timeout_us = dev->vctl_timeout_us;
246 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS0);
247 while (msm_spm_drv_get_sts_pmic_state(dev) != MSM_SPM_PMIC_STATE_IDLE) {
248 if (!timeout_us)
249 goto set_vdd_bail;
250
251 if (timeout_us > 10) {
252 udelay(10);
253 timeout_us -= 10;
254 } else {
255 udelay(timeout_us);
256 timeout_us = 0;
257 }
258 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS0);
259 }
260
261 msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW2_STS1);
262
263 if (msm_spm_drv_get_sts_curr_pmic_data(dev) != vlevel)
264 goto set_vdd_bail;
265
266 if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
267 pr_info("%s: done, remaining timeout %uus\n",
268 __func__, timeout_us);
269
270 return 0;
271
272set_vdd_bail:
273 pr_err("%s: failed, remaining timeout %uus, vlevel 0x%x\n",
274 __func__, timeout_us, msm_spm_drv_get_sts_curr_pmic_data(dev));
275 return -EIO;
276}
277
Maheshkumar Sivasubramanian4ac23762011-11-02 10:03:06 -0600278void msm_spm_drv_reinit(struct msm_spm_driver_data *dev)
279{
280 int i;
281
282 for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
283 msm_spm_drv_flush_shadow(dev, i);
284
285 msm_spm_drv_flush_seq_entry(dev);
286 mb();
287}
288
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700289int __init msm_spm_drv_init(struct msm_spm_driver_data *dev,
290 struct msm_spm_platform_data *data)
291{
292
293 int i;
294 int num_spm_entry;
295
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 BUG_ON(!dev || !data);
297
298 dev->reg_base_addr = data->reg_base_addr;
299 memcpy(dev->reg_shadow, data->reg_init_values,
300 sizeof(data->reg_init_values));
301
302 dev->vctl_timeout_us = data->vctl_timeout_us;
303
304 for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
305 msm_spm_drv_flush_shadow(dev, i);
306 /* barrier to ensure write completes before we update shadow
307 * registers
308 */
309 mb();
310
311 for (i = 0; i < MSM_SPM_REG_NR_INITIALIZE; i++)
312 msm_spm_drv_load_shadow(dev, i);
313
314 /* barrier to ensure read completes before we proceed further*/
315 mb();
316
317 num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
318
319 dev->reg_seq_entry_shadow =
320 kmalloc(sizeof(*dev->reg_seq_entry_shadow) * num_spm_entry,
321 GFP_KERNEL);
322
323 if (!dev->reg_seq_entry_shadow)
324 return -ENOMEM;
325
326
327 memset(dev->reg_seq_entry_shadow, 0x0f,
328 num_spm_entry * sizeof(*dev->reg_seq_entry_shadow));
329
330 return 0;
331}