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