blob: dd6ffabe1bd17d152274a458f947966b0b971496 [file] [log] [blame]
Kaushal Kumar5323ddc2012-09-18 17:12:43 +05301/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +05302 *
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#define pr_fmt(fmt) "%s: " fmt, __func__
15
16#include <linux/module.h>
Kaushal Kumar5d83a9e2012-09-07 16:34:02 +053017#include <linux/moduleparam.h>
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +053018#include <linux/kernel.h>
19#include <linux/io.h>
20#include <linux/irq.h>
21#include <linux/init.h>
22#include <linux/slab.h>
23#include <linux/delay.h>
24#include <linux/errno.h>
25#include <linux/debugfs.h>
26#include <linux/interrupt.h>
27#include <linux/platform_device.h>
28#include <linux/cpufreq.h>
29#include <linux/iopoll.h>
30#include <linux/delay.h>
31#include <linux/regulator/consumer.h>
32
33#include <mach/irqs.h>
34
35#include "msm_cpr.h"
36
37#define MODULE_NAME "msm-cpr"
38
Kaushal Kumard0e4c812012-08-22 16:30:09 +053039/**
40 * Convert the Delay time to Timer Count Register
41 * e.g if frequency is 19200 kHz and delay required is
42 * 20000us, so timer count will be 19200 * 20000 / 1000
43 */
44#define TIMER_COUNT(freq, delay) ((freq * delay) / 1000)
45#define ALL_CPR_IRQ 0x3F
Kaushal Kumarbef21962012-09-06 18:44:08 +053046#define STEP_QUOT_MAX 25
47#define STEP_QUOT_MIN 12
Kaushal Kumard0e4c812012-08-22 16:30:09 +053048
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +053049/* Need platform device handle for suspend and resume APIs */
50static struct platform_device *cpr_pdev;
51
Kaushal Kumar5d83a9e2012-09-07 16:34:02 +053052static bool enable = 1;
Kaushal Kumarecfc9182012-10-25 16:17:51 +053053static bool disable_cpr;
Kaushal Kumar5d83a9e2012-09-07 16:34:02 +053054module_param(enable, bool, 0644);
55MODULE_PARM_DESC(enable, "CPR Enable");
56
Kaushal Kumarc958cecd2012-09-25 08:44:28 +053057static int msm_cpr_debug_mask;
58module_param_named(
59 debug_mask, msm_cpr_debug_mask, int, S_IRUGO | S_IWUSR
60);
61
62enum {
63 /* configuration log */
64 MSM_CPR_DEBUG_CONFIG = BIT(0),
65 /* step up/down interrupt log */
66 MSM_CPR_DEBUG_STEPS = BIT(1),
67 /* cpu frequency notification log */
68 MSM_CPR_DEBUG_FREQ_TRANS = BIT(2),
69};\
70
71#define msm_cpr_debug(mask, message, ...) \
72 do { \
73 if ((mask) & msm_cpr_debug_mask) \
74 pr_info(message, ##__VA_ARGS__); \
75 } while (0)
76
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +053077struct msm_cpr {
78 int curr_osc;
79 int cpr_mode;
80 int prev_mode;
81 uint32_t floor;
82 uint32_t ceiling;
Kaushal Kumard0e4c812012-08-22 16:30:09 +053083 bool max_volt_set;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +053084 void __iomem *base;
85 unsigned int irq;
Trilok Sonidb64ae92012-09-02 02:09:04 +053086 uint32_t cur_Vmin;
87 uint32_t cur_Vmax;
Kaushal Kumar5323ddc2012-09-18 17:12:43 +053088 uint32_t prev_volt_uV;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +053089 struct mutex cpr_mutex;
Kaushal Kumarea80e642012-09-24 12:50:43 +053090 spinlock_t cpr_lock;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +053091 struct regulator *vreg_cx;
92 const struct msm_cpr_config *config;
93 struct notifier_block freq_transition;
Tirupathi Reddy7c5d7b12012-11-06 13:29:36 +053094 uint32_t step_size;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +053095};
96
97/* Need to maintain state data for suspend and resume APIs */
98static struct msm_cpr_reg cpr_save_state;
99
100static inline
101void cpr_write_reg(struct msm_cpr *cpr, u32 offset, u32 value)
102{
103 writel_relaxed(value, cpr->base + offset);
104}
105
106static inline u32 cpr_read_reg(struct msm_cpr *cpr, u32 offset)
107{
108 return readl_relaxed(cpr->base + offset);
109}
110
111static
112void cpr_modify_reg(struct msm_cpr *cpr, u32 offset, u32 mask, u32 value)
113{
114 u32 reg_val;
115
116 reg_val = readl_relaxed(cpr->base + offset);
117 reg_val &= ~mask;
118 reg_val |= value;
119 writel_relaxed(reg_val, cpr->base + offset);
120}
121
122#ifdef DEBUG
123static void cpr_regs_dump_all(struct msm_cpr *cpr)
124{
125 pr_debug("RBCPR_GCNT_TARGET(%d): 0x%x\n",
126 cpr->curr_osc, readl_relaxed(cpr->base +
127 RBCPR_GCNT_TARGET(cpr->curr_osc)));
128 pr_debug("RBCPR_TIMER_INTERVAL: 0x%x\n",
129 readl_relaxed(cpr->base + RBCPR_TIMER_INTERVAL));
130 pr_debug("RBIF_TIMER_ADJUST: 0x%x\n",
131 readl_relaxed(cpr->base + RBIF_TIMER_ADJUST));
132 pr_debug("RBIF_LIMIT: 0x%x\n",
133 readl_relaxed(cpr->base + RBIF_LIMIT));
134 pr_debug("RBCPR_STEP_QUOT: 0x%x\n",
135 readl_relaxed(cpr->base + RBCPR_STEP_QUOT));
136 pr_debug("RBIF_SW_VLEVEL: 0x%x\n",
137 readl_relaxed(cpr->base + RBIF_SW_VLEVEL));
138 pr_debug("RBCPR_DEBUG1: 0x%x\n",
139 readl_relaxed(cpr->base + RBCPR_DEBUG1));
140 pr_debug("RBCPR_RESULT_0: 0x%x\n",
141 readl_relaxed(cpr->base + RBCPR_RESULT_0));
142 pr_debug("RBCPR_RESULT_1: 0x%x\n",
143 readl_relaxed(cpr->base + RBCPR_RESULT_1));
144 pr_debug("RBCPR_QUOT_AVG: 0x%x\n",
145 readl_relaxed(cpr->base + RBCPR_QUOT_AVG));
146 pr_debug("RBCPR_CTL: 0x%x\n",
147 readl_relaxed(cpr->base + RBCPR_CTL));
148 pr_debug("RBIF_IRQ_EN(0): 0x%x\n",
149 cpr_read_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line)));
150 pr_debug("RBIF_IRQ_STATUS: 0x%x\n",
151 cpr_read_reg(cpr, RBIF_IRQ_STATUS));
152}
153#endif
154
155/* Enable the CPR H/W Block */
156static void cpr_enable(struct msm_cpr *cpr)
157{
Kaushal Kumarea80e642012-09-24 12:50:43 +0530158 spin_lock(&cpr->cpr_lock);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530159 cpr_modify_reg(cpr, RBCPR_CTL, LOOP_EN_M, ENABLE_CPR);
Kaushal Kumarea80e642012-09-24 12:50:43 +0530160 spin_unlock(&cpr->cpr_lock);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530161}
162
163/* Disable the CPR H/W Block */
164static void cpr_disable(struct msm_cpr *cpr)
165{
Kaushal Kumarea80e642012-09-24 12:50:43 +0530166 spin_lock(&cpr->cpr_lock);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530167 cpr_modify_reg(cpr, RBCPR_CTL, LOOP_EN_M, DISABLE_CPR);
Kaushal Kumarea80e642012-09-24 12:50:43 +0530168 spin_unlock(&cpr->cpr_lock);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530169}
170
171static int32_t cpr_poll_result(struct msm_cpr *cpr)
172{
173 uint32_t val = 0;
174 int8_t rc = 0;
175
176 rc = readl_poll_timeout(cpr->base + RBCPR_RESULT_0, val, ~val & BUSY_M,
177 10, 1000);
178 if (rc)
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530179 pr_err("RBCPR_RESULT_0 read error: %d\n", rc);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530180 return rc;
181}
182
183static int32_t cpr_poll_result_done(struct msm_cpr *cpr)
184{
185 uint32_t val = 0;
186 int8_t rc = 0;
187
188 rc = readl_poll_timeout(cpr->base + RBIF_IRQ_STATUS, val, val & 0x1,
189 10, 1000);
190 if (rc)
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530191 pr_err("RBCPR_IRQ_STATUS read error: %d\n", rc);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530192 return rc;
193}
194
195static void
196cpr_2pt_kv_analysis(struct msm_cpr *cpr, struct msm_cpr_mode *chip_data)
197{
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530198 int32_t level_uV = 0, rc;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530199 uint32_t quot1, quot2;
200
201 /**
202 * 2 Point KV Analysis to calculate Step Quot
203 * STEP_QUOT is number of QUOT units per PMIC step
204 * STEP_QUOT = (quot1 - quot2) / 4
205 *
206 * The step quot is calculated once for every mode and stored for
207 * later use.
208 */
209 if (chip_data->step_quot != ~0)
210 goto out_2pt_kv;
211
212 /**
213 * Using the value from chip_data->tgt_volt_offset
214 * calculate the new PMIC adjusted voltages and set
215 * the PMIC to provide this value.
216 *
217 * Assuming default voltage is the highest value of safe boot up
218 * voltage, offset is always subtracted from it.
219 *
220 */
Trilok Sonidb64ae92012-09-02 02:09:04 +0530221 level_uV = chip_data->turbo_Vmax -
Tirupathi Reddy7c5d7b12012-11-06 13:29:36 +0530222 (chip_data->tgt_volt_offset * cpr->step_size);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530223 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
224 "tgt_volt_uV = %d\n", level_uV);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530225
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530226 /* Call the PMIC specific routine to set the voltage */
227 rc = regulator_set_voltage(cpr->vreg_cx, level_uV, level_uV);
228 if (rc) {
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530229 pr_err("Initial voltage set at %duV failed\n", level_uV);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530230 return;
231 }
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530232
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530233 rc = regulator_enable(cpr->vreg_cx);
234 if (rc) {
235 pr_err("failed to enable %s, rc=%d\n", "vdd_cx", rc);
236 return;
237 }
238
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530239 /* First CPR measurement at a higher voltage to get QUOT1 */
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530240
241 /* Enable the Software mode of operation */
242 cpr_modify_reg(cpr, RBCPR_CTL, HW_TO_PMIC_EN_M, SW_MODE);
243
244 /* Enable the cpr measurement */
245 cpr_modify_reg(cpr, RBCPR_CTL, LOOP_EN_M, ENABLE_CPR);
246
247 /* IRQ is already disabled */
248 rc = cpr_poll_result_done(cpr);
249 if (rc) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530250 pr_err("Quot1: Exiting due to INT_DONE poll timeout\n");
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530251 return;
252 }
253
254 rc = cpr_poll_result(cpr);
255 if (rc) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530256 pr_err("Quot1: Exiting due to BUSY poll timeout\n");
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530257 return;
258 }
259
260 quot1 = (cpr_read_reg(cpr, RBCPR_DEBUG1) & QUOT_SLOW_M) >> 12;
261
262 /* Take second CPR measurement at a lower voltage to get QUOT2 */
Tirupathi Reddy7c5d7b12012-11-06 13:29:36 +0530263 level_uV -= 4 * cpr->step_size;
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530264 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
265 "tgt_volt_uV = %d\n", level_uV);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530266
267 cpr_modify_reg(cpr, RBCPR_CTL, LOOP_EN_M, DISABLE_CPR);
268 /* Call the PMIC specific routine to set the voltage */
269 rc = regulator_set_voltage(cpr->vreg_cx, level_uV, level_uV);
270 if (rc) {
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530271 pr_err("Voltage set at %duV failed\n", level_uV);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530272 return;
273 }
274
275 cpr_modify_reg(cpr, RBCPR_CTL, HW_TO_PMIC_EN_M, SW_MODE);
276 cpr_modify_reg(cpr, RBCPR_CTL, LOOP_EN_M, ENABLE_CPR);
277
278 /* cpr_write_reg(cpr, RBIF_CONT_NACK_CMD, 0x1); */
279 rc = cpr_poll_result_done(cpr);
280 if (rc) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530281 pr_err("Quot2: Exiting due to INT_DONE poll timeout\n");
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530282 goto err_poll_result_done;
283 }
284 /* IRQ is already disabled */
285 rc = cpr_poll_result(cpr);
286 if (rc) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530287 pr_err("Quot2: Exiting due to BUSY poll timeout\n");
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530288 goto err_poll_result;
289 }
290 quot2 = (cpr_read_reg(cpr, RBCPR_DEBUG1) & QUOT_SLOW_M) >> 12;
Kaushal Kumarbef21962012-09-06 18:44:08 +0530291 /*
292 * Based on chip characterization data, it is good to add some
293 * margin on top of calculated step quot to help reduce the
294 * number of CPR interrupts. The present value suggested is 3.
295 * Further, if the step quot is outside range, clamp it to the
296 * maximum permitted value.
297 */
298 chip_data->step_quot = ((quot1 - quot2) / 4) + 3;
299 if (chip_data->step_quot < STEP_QUOT_MIN ||
300 chip_data->step_quot > STEP_QUOT_MAX)
301 chip_data->step_quot = STEP_QUOT_MAX;
302
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530303 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
304 "Step Quot is %d\n", chip_data->step_quot);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530305 /* Disable the cpr */
306 cpr_modify_reg(cpr, RBCPR_CTL, LOOP_EN_M, DISABLE_CPR);
307
308out_2pt_kv:
309 /* Program the step quot */
310 cpr_write_reg(cpr, RBCPR_STEP_QUOT, (chip_data->step_quot & 0xFF));
311 return;
312err_poll_result:
313err_poll_result_done:
314 regulator_disable(cpr->vreg_cx);
315}
316
317static inline
318void cpr_irq_clr_and_ack(struct msm_cpr *cpr, uint32_t mask)
319{
320 /* Clear the interrupt */
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530321 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530322 /* Acknowledge the Recommendation */
323 cpr_write_reg(cpr, RBIF_CONT_ACK_CMD, 0x1);
324}
325
326static inline
327void cpr_irq_clr_and_nack(struct msm_cpr *cpr, uint32_t mask)
328{
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530329 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530330 cpr_write_reg(cpr, RBIF_CONT_NACK_CMD, 0x1);
331}
332
Kaushal Kumarecfc9182012-10-25 16:17:51 +0530333static void cpr_irq_set(struct msm_cpr *cpr, uint32_t irq, bool enable_irq)
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530334{
335 uint32_t irq_enabled;
336
337 irq_enabled = cpr_read_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line));
Kaushal Kumarecfc9182012-10-25 16:17:51 +0530338 if (enable_irq == 1)
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530339 irq_enabled |= irq;
340 else
341 irq_enabled &= ~irq;
342 cpr_modify_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line),
343 INT_MASK, irq_enabled);
344}
345
346static void
347cpr_up_event_handler(struct msm_cpr *cpr, uint32_t new_volt)
348{
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530349 int set_volt_uV, rc;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530350 struct msm_cpr_mode *chip_data;
351
352 chip_data = &cpr->config->cpr_mode_data[cpr->cpr_mode];
353
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530354 /* Set New PMIC voltage */
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530355 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
356 "current Vmin=%d Vmax=%d\n", cpr->cur_Vmin, cpr->cur_Vmax);
Trilok Sonidb64ae92012-09-02 02:09:04 +0530357 set_volt_uV = (new_volt < cpr->cur_Vmax ? new_volt
358 : cpr->cur_Vmax);
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530359
360 if (cpr->prev_volt_uV == set_volt_uV)
361 rc = regulator_sync_voltage(cpr->vreg_cx);
362 else
363 rc = regulator_set_voltage(cpr->vreg_cx, set_volt_uV,
364 set_volt_uV);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530365 if (rc) {
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530366 pr_err("Unable to set_voltage = %d, rc(%d)\n", set_volt_uV, rc);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530367 cpr_irq_clr_and_nack(cpr, BIT(4) | BIT(0));
368 return;
369 }
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530370
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530371 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
372 "(railway_voltage: %d uV)\n", set_volt_uV);
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530373 cpr->prev_volt_uV = set_volt_uV;
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530374
Trilok Sonidb64ae92012-09-02 02:09:04 +0530375 cpr->max_volt_set = (set_volt_uV == cpr->cur_Vmax) ? 1 : 0;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530376
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530377 /* Clear all the interrupts */
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530378 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530379
380 /* Disable Auto ACK for Down interrupts */
381 cpr_modify_reg(cpr, RBCPR_CTL, SW_AUTO_CONT_NACK_DN_EN_M, 0);
382
383 /* Enable down interrupts to App as it might have got disabled if CPR
384 * hit Vmin earlier. Voltage set is above Vmin now.
385 */
386 cpr_irq_set(cpr, DOWN_INT, 1);
387
388 /* Acknowledge the Recommendation */
389 cpr_write_reg(cpr, RBIF_CONT_ACK_CMD, 0x1);
390}
391
392static void
393cpr_dn_event_handler(struct msm_cpr *cpr, uint32_t new_volt)
394{
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530395 int set_volt_uV, rc;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530396 struct msm_cpr_mode *chip_data;
397
398 chip_data = &cpr->config->cpr_mode_data[cpr->cpr_mode];
399
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530400 /* Set New PMIC volt */
Trilok Sonidb64ae92012-09-02 02:09:04 +0530401 set_volt_uV = (new_volt > cpr->cur_Vmin ? new_volt
402 : cpr->cur_Vmin);
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530403
404 if (cpr->prev_volt_uV == set_volt_uV)
405 rc = regulator_sync_voltage(cpr->vreg_cx);
406 else
407 rc = regulator_set_voltage(cpr->vreg_cx, set_volt_uV,
408 set_volt_uV);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530409 if (rc) {
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530410 pr_err("Unable to set_voltage = %d, rc(%d)\n", set_volt_uV, rc);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530411 cpr_irq_clr_and_nack(cpr, BIT(2) | BIT(0));
412 return;
413 }
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530414
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530415 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
416 "(railway_voltage: %d uV)\n", set_volt_uV);
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530417 cpr->prev_volt_uV = set_volt_uV;
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530418
419 cpr->max_volt_set = 0;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530420
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530421 /* Clear all the interrupts */
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530422 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530423
Trilok Sonidb64ae92012-09-02 02:09:04 +0530424 if (new_volt <= cpr->cur_Vmin) {
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530425 /*
426 * Disable down interrupt to App after we hit Vmin
427 * It shall be enabled after we service an up interrupt
428 *
429 * A race condition between freq switch handler and CPR
430 * interrupt handler is possible. So, do not disable
431 * interrupt if a freq switch already caused a mode
432 * change since we need this interrupt in the new mode.
433 */
434 if (cpr->cpr_mode == cpr->prev_mode) {
435 /* Enable Auto ACK for CPR Down Flags
436 * while DOWN_INT to App is disabled */
437 cpr_modify_reg(cpr, RBCPR_CTL,
438 SW_AUTO_CONT_NACK_DN_EN_M,
439 SW_AUTO_CONT_NACK_DN_EN);
440 cpr_irq_set(cpr, DOWN_INT, 0);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530441 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
442 "DOWN_INT disabled\n");
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530443 }
444 }
445 /* Acknowledge the Recommendation */
446 cpr_write_reg(cpr, RBIF_CONT_ACK_CMD, 0x1);
447}
448
449static void cpr_set_vdd(struct msm_cpr *cpr, enum cpr_action action)
450{
451 uint32_t curr_volt, new_volt, error_step;
452 struct msm_cpr_mode *chip_data;
453
454 chip_data = &cpr->config->cpr_mode_data[cpr->cpr_mode];
455 error_step = cpr_read_reg(cpr, RBCPR_RESULT_0) >> 2;
Trilok Soni18a226c2012-09-02 01:34:17 +0530456
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530457 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
458 "RBCPR_RESULT_0 17:6=%d\n", (cpr_read_reg(cpr,
459 RBCPR_RESULT_0) >> 6) & 0xFFF);
460 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
461 "RBCPR_RESULT_0 Busy_b19=%d\n", (cpr_read_reg(cpr,
462 RBCPR_RESULT_0) >> 19) & 0x1);
463
464 error_step &= 0xF;
Trilok Soni18a226c2012-09-02 01:34:17 +0530465 curr_volt = regulator_get_voltage(cpr->vreg_cx);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530466 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
467 "Current voltage=%d\n", curr_volt);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530468
469 if (action == UP) {
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530470 /* Clear IRQ, ACK and return if Vdd already at Vmax */
471 if (cpr->max_volt_set == 1) {
472 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
473 cpr_write_reg(cpr, RBIF_CONT_NACK_CMD, 0x1);
474 return;
475 }
476
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530477 /**
478 * Using up margin in the comparison helps avoid having to
479 * change up threshold values in chip register.
480 */
481 if (error_step < (cpr->config->up_threshold +
482 cpr->config->up_margin)) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530483 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
484 "UP_INT error step too small to set\n");
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530485 cpr_irq_clr_and_nack(cpr, BIT(4) | BIT(0));
486 return;
487 }
488
Kaushal Kumar210c2f32012-09-18 19:42:05 +0530489 /**
490 * As per chip characterization recommendation, add a step
491 * to up error steps to increase system stability
492 */
493 error_step += 1;
494
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530495 /* Calculte new PMIC voltage */
Tirupathi Reddy7c5d7b12012-11-06 13:29:36 +0530496 new_volt = curr_volt + (error_step * cpr->step_size);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530497 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
498 "UP_INT: new_volt: %d, error_step=%d\n",
499 new_volt, error_step);
500 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
501 "Current RBCPR_GCNT_TARGET(%d): = 0x%x\n",
502 cpr->curr_osc, readl_relaxed(cpr->base +
503 RBCPR_GCNT_TARGET(cpr->curr_osc)) & TARGET_M);
504 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
505 "(UP Voltage recommended by CPR: %d uV)\n", new_volt);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530506 cpr_up_event_handler(cpr, new_volt);
507
508 } else if (action == DOWN) {
509 /**
510 * Using down margin in the comparison helps avoid having to
511 * change down threshold values in chip register.
512 */
513 if (error_step < (cpr->config->dn_threshold +
514 cpr->config->dn_margin)) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530515 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
516 "DOWN_INT error_step=%d is too small to set\n",
517 error_step);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530518 cpr_irq_clr_and_nack(cpr, BIT(2) | BIT(0));
519 return;
520 }
521
Kaushal Kumar210c2f32012-09-18 19:42:05 +0530522 /**
523 * As per chip characterization recommendation, deduct 2 steps
524 * from down error steps to decrease chances of getting closer
525 * to the system level Vmin, thereby improving stability
526 */
527 error_step -= 2;
528
Kaushal Kumar358861e2012-10-05 07:13:37 +0530529 /* Keep down step upto two per interrupt to avoid any spike */
530 if (error_step > 2)
531 error_step = 2;
532
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530533 /* Calculte new PMIC voltage */
Tirupathi Reddy7c5d7b12012-11-06 13:29:36 +0530534 new_volt = curr_volt - (error_step * cpr->step_size);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530535 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
536 "DOWN_INT: new_volt: %d, error_step=%d\n",
537 new_volt, error_step);
538 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
539 "Current RBCPR_GCNT_TARGET(%d): = 0x%x\n",
540 cpr->curr_osc, readl_relaxed(cpr->base +
541 RBCPR_GCNT_TARGET(cpr->curr_osc)) & TARGET_M);
542 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
543 "(DN Voltage recommended by CPR: %d uV)\n", new_volt);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530544 cpr_dn_event_handler(cpr, new_volt);
545 }
546}
547
548static irqreturn_t cpr_irq0_handler(int irq, void *dev_id)
549{
550 struct msm_cpr *cpr = dev_id;
551 uint32_t reg_val, ctl_reg;
552
553 reg_val = cpr_read_reg(cpr, RBIF_IRQ_STATUS);
554 ctl_reg = cpr_read_reg(cpr, RBCPR_CTL);
555
556 /* Following sequence of handling is as per each IRQ's priority */
557 if (reg_val & BIT(4)) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530558 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
559 "CPR:IRQ %d occured for UP Flag\n", irq);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530560 cpr_set_vdd(cpr, UP);
561
562 } else if ((reg_val & BIT(2)) && !(ctl_reg & SW_AUTO_CONT_NACK_DN_EN)) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530563 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
564 "CPR:IRQ %d occured for Down Flag\n", irq);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530565 cpr_set_vdd(cpr, DOWN);
566
567 } else if (reg_val & BIT(1)) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530568 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
569 "CPR:IRQ %d occured for Min Flag\n", irq);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530570 cpr_irq_clr_and_nack(cpr, BIT(1) | BIT(0));
571
572 } else if (reg_val & BIT(5)) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530573 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
574 "CPR:IRQ %d occured for MAX Flag\n", irq);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530575 cpr_irq_clr_and_nack(cpr, BIT(5) | BIT(0));
576
577 } else if (reg_val & BIT(3)) {
578 /* SW_AUTO_CONT_ACK_EN is enabled */
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530579 msm_cpr_debug(MSM_CPR_DEBUG_STEPS,
580 "CPR:IRQ %d occured for Mid Flag\n", irq);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530581 }
582 return IRQ_HANDLED;
583}
584
585static void cpr_config(struct msm_cpr *cpr)
586{
Kaushal Kumarff3b8bb2012-09-20 20:03:34 +0530587 uint32_t delay_count, cnt = 0, rc;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530588 struct msm_cpr_mode *chip_data;
589
590 chip_data = &cpr->config->cpr_mode_data[cpr->cpr_mode];
591
592 /* Program the SW vlevel */
593 cpr_modify_reg(cpr, RBIF_SW_VLEVEL, SW_VLEVEL_M,
594 cpr->config->sw_vlevel);
595
596 /* Set the floor and ceiling values */
597 cpr->floor = cpr->config->floor;
598 cpr->ceiling = cpr->config->ceiling;
599
600 /* Program the Ceiling & Floor values */
601 cpr_modify_reg(cpr, RBIF_LIMIT, (CEILING_M | FLOOR_M),
602 ((cpr->ceiling << 6) | cpr->floor));
603
604 /* Program the Up and Down Threshold values */
605 cpr_modify_reg(cpr, RBCPR_CTL, UP_THRESHOLD_M | DN_THRESHOLD_M,
606 cpr->config->up_threshold << 24 |
607 cpr->config->dn_threshold << 28);
608
609 cpr->curr_osc = chip_data->ring_osc;
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530610 chip_data->ring_osc_data[cpr->curr_osc].quot =
611 cpr->config->max_quot;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530612
613 /**
614 * Program the gate count and target values
615 * for all the ring oscilators
616 */
617 while (cnt < NUM_OSC) {
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530618 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
619 "Prog:cnt(%d) gcnt=0x%x quot=0x%x\n", cnt,
620 chip_data->ring_osc_data[cnt].gcnt,
621 chip_data->ring_osc_data[cnt].quot);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530622 cpr_modify_reg(cpr, RBCPR_GCNT_TARGET(cnt),
623 (GCNT_M | TARGET_M),
624 (chip_data->ring_osc_data[cnt].gcnt << 12 |
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530625 chip_data->ring_osc_data[cnt].quot));
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530626 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
627 "RBCPR_GCNT_TARGET(%d): = 0x%x\n", cnt,
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530628 readl_relaxed(cpr->base + RBCPR_GCNT_TARGET(cnt)));
629 cnt++;
630 }
631
632 /* Configure the step quot */
633 cpr_2pt_kv_analysis(cpr, chip_data);
634
Kaushal Kumarff3b8bb2012-09-20 20:03:34 +0530635 /* Call the PMIC specific routine to set the voltage */
636 rc = regulator_set_voltage(cpr->vreg_cx, chip_data->calibrated_uV,
637 chip_data->calibrated_uV);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530638 if (rc)
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530639 pr_err("Voltage set failed %d\n", rc);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530640
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530641 /*
642 * Program the Timer Register for delay between CPR measurements
643 * This is required to allow the device sufficient time for idle
644 * power collapse.
645 */
646 delay_count = TIMER_COUNT(cpr->config->ref_clk_khz,
647 cpr->config->delay_us);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530648 cpr_write_reg(cpr, RBCPR_TIMER_INTERVAL, delay_count);
649
Kaushal Kumara6bad692012-09-27 06:52:19 +0530650 /* Use Consecutive Down to avoid any interrupt due to spike */
651 cpr_write_reg(cpr, RBIF_TIMER_ADJUST, (0x2 << RBIF_CONS_DN_SHIFT));
652 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG, "RBIF_TIMER_ADJUST: 0x%x\n",
653 readl_relaxed(cpr->base + RBIF_TIMER_ADJUST));
654
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530655 /* Enable the Timer */
656 cpr_modify_reg(cpr, RBCPR_CTL, TIMER_M, ENABLE_TIMER);
657
658 /* Enable Auto ACK for Mid interrupts */
659 cpr_modify_reg(cpr, RBCPR_CTL, SW_AUTO_CONT_ACK_EN_M,
660 SW_AUTO_CONT_ACK_EN);
661}
662
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530663static int
664cpr_freq_transition(struct notifier_block *nb, unsigned long val,
665 void *data)
666{
667 struct msm_cpr *cpr = container_of(nb, struct msm_cpr, freq_transition);
668 struct cpufreq_freqs *freqs = data;
Trilok Sonic43d1122012-09-02 01:48:33 +0530669 uint32_t quot, new_freq, ctl_reg;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530670
671 switch (val) {
672 case CPUFREQ_PRECHANGE:
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530673 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
674 "pre freq change notification to cpr\n");
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530675 /* Disable Measurement to stop generation of CPR IRQs */
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530676 cpr_disable(cpr);
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530677 /* Disable routing of IRQ to App */
678 cpr_irq_set(cpr, INT_MASK & ~MID_INT, 0);
679 disable_irq(cpr->irq);
680 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530681
682 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
683 "RBCPR_CTL: 0x%x\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530684 readl_relaxed(cpr->base + RBCPR_CTL));
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530685 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
686 "RBIF_IRQ_STATUS: 0x%x\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530687 cpr_read_reg(cpr, RBIF_IRQ_STATUS));
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530688 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
689 "RBIF_IRQ_EN(0): 0x%x\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530690 cpr_read_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line)));
691
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530692 cpr->prev_mode = cpr->cpr_mode;
693 break;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530694
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530695 case CPUFREQ_POSTCHANGE:
696 pr_debug("post freq change notification to cpr\n");
Trilok Sonic43d1122012-09-02 01:48:33 +0530697 ctl_reg = cpr_read_reg(cpr, RBCPR_CTL);
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530698 /**
699 * As per chip characterization data, use max nominal freq
700 * to calculate quot for all lower frequencies too
701 */
Trilok Sonidb64ae92012-09-02 02:09:04 +0530702 if (freqs->new > cpr->config->max_nom_freq) {
703 new_freq = freqs->new;
704 cpr->cur_Vmin = cpr->config->cpr_mode_data[1].turbo_Vmin;
705 cpr->cur_Vmax = cpr->config->cpr_mode_data[1].turbo_Vmax;
706 } else {
707 new_freq = cpr->config->max_nom_freq;
708 cpr->cur_Vmin = cpr->config->cpr_mode_data[1].nom_Vmin;
709 cpr->cur_Vmax = cpr->config->cpr_mode_data[1].nom_Vmax;
710 }
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530711
712 /* Configure CPR for the new frequency */
713 quot = cpr->config->get_quot(cpr->config->max_quot,
714 cpr->config->max_freq / 1000,
715 new_freq / 1000);
716 cpr_modify_reg(cpr, RBCPR_GCNT_TARGET(cpr->curr_osc), TARGET_M,
717 quot);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530718 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
719 "RBCPR_GCNT_TARGET(%d): = 0x%x\n", cpr->curr_osc,
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530720 readl_relaxed(cpr->base +
721 RBCPR_GCNT_TARGET(cpr->curr_osc)));
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530722 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
723 "new_freq: %d, quot_freq: %d, quot: %d\n",
724 freqs->new, new_freq, quot);
725 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
726 "PVS Voltage setting is: %d\n",
Kaushal Kumar549c7222012-09-11 19:21:26 +0530727 regulator_get_voltage(cpr->vreg_cx));
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530728
729 enable_irq(cpr->irq);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530730 /**
731 * Enable all interrupts. One of them could be in a disabled
732 * state if vdd had hit Vmax / Vmin earlier
733 */
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530734 cpr_irq_set(cpr, INT_MASK & ~MID_INT, 1);
Trilok Sonic43d1122012-09-02 01:48:33 +0530735
736 /**
737 * Clear the auto NACK down bit if enabled in the freq.
738 * transition phase.
739 */
740 if (ctl_reg & SW_AUTO_CONT_NACK_DN_EN)
741 cpr_modify_reg(cpr, RBCPR_CTL,
742 SW_AUTO_CONT_NACK_DN_EN_M, 0);
Kaushal Kumar5323ddc2012-09-18 17:12:43 +0530743 if (cpr->max_volt_set)
744 cpr->max_volt_set = 0;
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530745
746 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
747 "RBIF_IRQ_EN(0): 0x%x\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530748 cpr_read_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line)));
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530749 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
750 "RBCPR_CTL: 0x%x\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530751 readl_relaxed(cpr->base + RBCPR_CTL));
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530752 msm_cpr_debug(MSM_CPR_DEBUG_FREQ_TRANS,
753 "RBIF_IRQ_STATUS: 0x%x\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530754 cpr_read_reg(cpr, RBIF_IRQ_STATUS));
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530755
Kaushal Kumar0aae7432012-10-05 07:25:20 +0530756 /* Clear all the interrupts */
757 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
758
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530759 cpr_enable(cpr);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530760 break;
761 default:
762 break;
763 }
Trilok Sonid77a8582012-09-23 00:54:46 +0530764 return NOTIFY_OK;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530765}
766
767#ifdef CONFIG_PM
768static int msm_cpr_resume(struct device *dev)
769{
770 struct msm_cpr *cpr = dev_get_drvdata(dev);
771 int osc_num = cpr->config->cpr_mode_data->ring_osc;
772
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530773 cpr->config->clk_enable();
774
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530775 cpr_write_reg(cpr, RBCPR_TIMER_INTERVAL,
776 cpr_save_state.rbif_timer_interval);
777 cpr_write_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line),
778 cpr_save_state.rbif_int_en);
779 cpr_write_reg(cpr, RBIF_LIMIT,
780 cpr_save_state.rbif_limit);
781 cpr_write_reg(cpr, RBIF_TIMER_ADJUST,
782 cpr_save_state.rbif_timer_adjust);
783 cpr_write_reg(cpr, RBCPR_GCNT_TARGET(osc_num),
784 cpr_save_state.rbcpr_gcnt_target);
785 cpr_write_reg(cpr, RBCPR_STEP_QUOT,
786 cpr_save_state.rbcpr_step_quot);
787 cpr_write_reg(cpr, RBIF_SW_VLEVEL,
788 cpr_save_state.rbif_sw_level);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530789 cpr_write_reg(cpr, RBCPR_CTL,
790 cpr_save_state.rbcpr_ctl);
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530791
Kaushal Kumar0aae7432012-10-05 07:25:20 +0530792 /* Clear all the interrupts */
793 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
794
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530795 enable_irq(cpr->irq);
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530796 cpr_enable(cpr);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530797
798 return 0;
799}
800
801static int msm_cpr_suspend(struct device *dev)
802
803{
804 struct msm_cpr *cpr = dev_get_drvdata(dev);
805 int osc_num = cpr->config->cpr_mode_data->ring_osc;
806
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530807 /* Disable CPR measurement before IRQ to avoid pending interrupts */
808 cpr_disable(cpr);
809 disable_irq(cpr->irq);
810
Kaushal Kumar0aae7432012-10-05 07:25:20 +0530811 /* Clear all the interrupts */
812 cpr_write_reg(cpr, RBIF_IRQ_CLEAR, ALL_CPR_IRQ);
813
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530814 cpr_save_state.rbif_timer_interval =
815 cpr_read_reg(cpr, RBCPR_TIMER_INTERVAL);
816 cpr_save_state.rbif_int_en =
817 cpr_read_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line));
818 cpr_save_state.rbif_limit =
819 cpr_read_reg(cpr, RBIF_LIMIT);
820 cpr_save_state.rbif_timer_adjust =
821 cpr_read_reg(cpr, RBIF_TIMER_ADJUST);
822 cpr_save_state.rbcpr_gcnt_target =
823 cpr_read_reg(cpr, RBCPR_GCNT_TARGET(osc_num));
824 cpr_save_state.rbcpr_step_quot =
825 cpr_read_reg(cpr, RBCPR_STEP_QUOT);
826 cpr_save_state.rbif_sw_level =
827 cpr_read_reg(cpr, RBIF_SW_VLEVEL);
828 cpr_save_state.rbcpr_ctl =
829 cpr_read_reg(cpr, RBCPR_CTL);
830
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530831 return 0;
832}
833
834void msm_cpr_pm_resume(void)
835{
Kaushal Kumarecfc9182012-10-25 16:17:51 +0530836 if (!enable || disable_cpr)
Trilok Soni014b8892012-09-23 00:21:13 +0530837 return;
838
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530839 msm_cpr_resume(&cpr_pdev->dev);
840}
841EXPORT_SYMBOL(msm_cpr_pm_resume);
842
843void msm_cpr_pm_suspend(void)
844{
Kaushal Kumarecfc9182012-10-25 16:17:51 +0530845 if (!enable || disable_cpr)
Trilok Soni014b8892012-09-23 00:21:13 +0530846 return;
847
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530848 msm_cpr_suspend(&cpr_pdev->dev);
849}
850EXPORT_SYMBOL(msm_cpr_pm_suspend);
851#endif
852
853void msm_cpr_disable(void)
854{
Trilok Soni014b8892012-09-23 00:21:13 +0530855 struct msm_cpr *cpr;
856
Kaushal Kumarecfc9182012-10-25 16:17:51 +0530857 if (!enable || disable_cpr)
Trilok Soni014b8892012-09-23 00:21:13 +0530858 return;
859
860 cpr = platform_get_drvdata(cpr_pdev);
861
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530862 cpr_disable(cpr);
863}
864EXPORT_SYMBOL(msm_cpr_disable);
865
866void msm_cpr_enable(void)
867{
Trilok Soni014b8892012-09-23 00:21:13 +0530868 struct msm_cpr *cpr;
869
Kaushal Kumarecfc9182012-10-25 16:17:51 +0530870 if (!enable || disable_cpr)
Trilok Soni014b8892012-09-23 00:21:13 +0530871 return;
872
873 cpr = platform_get_drvdata(cpr_pdev);
874
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530875 cpr_enable(cpr);
876}
877EXPORT_SYMBOL(msm_cpr_enable);
878
879static int __devinit msm_cpr_probe(struct platform_device *pdev)
880{
881 int res, irqn, irq_enabled;
882 struct msm_cpr *cpr;
883 const struct msm_cpr_config *pdata = pdev->dev.platform_data;
884 void __iomem *base;
885 struct resource *mem;
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530886 struct msm_cpr_mode *chip_data;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530887
Kaushal Kumar5d83a9e2012-09-07 16:34:02 +0530888 if (!enable)
889 return -EPERM;
890
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530891 if (!pdata) {
892 pr_err("CPR: Platform data is not available\n");
Trilok Soni243960a2012-09-23 00:29:37 +0530893 enable = false;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530894 return -EIO;
895 }
896
Kaushal Kumarecfc9182012-10-25 16:17:51 +0530897 if (pdata->disable_cpr == true) {
898 pr_err("CPR disabled by modem\n");
899 disable_cpr = true;
900 return -EPERM;
901 }
902
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530903 cpr = devm_kzalloc(&pdev->dev, sizeof(struct msm_cpr), GFP_KERNEL);
Trilok Soni243960a2012-09-23 00:29:37 +0530904 if (!cpr) {
905 enable = false;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530906 return -ENOMEM;
Trilok Soni243960a2012-09-23 00:29:37 +0530907 }
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530908
Tirupathi Reddy001f6db2012-11-01 14:37:57 +0530909 /* enable clk for cpr */
910 if (!pdata->clk_enable) {
911 pr_err("CPR: Invalid clk_enable hook\n");
912 return -EFAULT;
913 }
914
915 pdata->clk_enable();
916
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530917 /* Initialize platform_data */
918 cpr->config = pdata;
919
Trilok Sonidb64ae92012-09-02 02:09:04 +0530920 /* Set initial Vmin,Vmax equal to turbo */
921 cpr->cur_Vmin = cpr->config->cpr_mode_data[1].turbo_Vmin;
922 cpr->cur_Vmax = cpr->config->cpr_mode_data[1].turbo_Vmax;
923
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530924 cpr_pdev = pdev;
925
926 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
927 if (!mem || !mem->start) {
928 pr_err("CPR: get resource failed\n");
929 res = -ENXIO;
930 goto out;
931 }
932
933 base = ioremap_nocache(mem->start, resource_size(mem));
934 if (!base) {
935 pr_err("CPR: ioremap failed\n");
936 res = -ENOMEM;
937 goto out;
938 }
939
940 if (cpr->config->irq_line < 0) {
941 pr_err("CPR: Invalid IRQ line specified\n");
942 res = -ENXIO;
943 goto err_ioremap;
944 }
945 irqn = platform_get_irq(pdev, cpr->config->irq_line);
946 if (irqn < 0) {
947 pr_err("CPR: Unable to get irq\n");
948 res = -ENXIO;
949 goto err_ioremap;
950 }
951
952 cpr->irq = irqn;
953
954 cpr->base = base;
955
Tirupathi Reddy7c5d7b12012-11-06 13:29:36 +0530956 cpr->step_size = pdata->step_size;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530957
Kaushal Kumarea80e642012-09-24 12:50:43 +0530958 spin_lock_init(&cpr->cpr_lock);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530959
960 /* Initialize the Voltage domain for CPR */
961 cpr->vreg_cx = regulator_get(&pdev->dev, "vddx_cx");
962 if (IS_ERR(cpr->vreg_cx)) {
963 res = PTR_ERR(cpr->vreg_cx);
964 pr_err("could not get regulator: %d\n", res);
965 goto err_reg_get;
966 }
967
968 /* Assume current mode is TURBO Mode */
969 cpr->cpr_mode = TURBO_MODE;
970 cpr->prev_mode = TURBO_MODE;
971
972 /* Initial configuration of CPR */
973 cpr_config(cpr);
974
975 platform_set_drvdata(pdev, cpr);
976
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530977 chip_data = &cpr->config->cpr_mode_data[cpr->cpr_mode];
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530978 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
979 "CPR Platform Data (upside_steps: %d) (downside_steps: %d))",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530980 cpr->config->up_threshold, cpr->config->dn_threshold);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530981 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
982 "(nominal_voltage: %duV) (turbo_voltage: %duV)\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530983 cpr->config->cpr_mode_data[NORMAL_MODE].calibrated_uV,
984 cpr->config->cpr_mode_data[TURBO_MODE].calibrated_uV);
Kaushal Kumarc958cecd2012-09-25 08:44:28 +0530985 msm_cpr_debug(MSM_CPR_DEBUG_CONFIG,
986 "(Current corner: TURBO) (gcnt_target: %d) (quot: %d)\n",
Kaushal Kumard0e4c812012-08-22 16:30:09 +0530987 chip_data->ring_osc_data[chip_data->ring_osc].gcnt,
988 chip_data->ring_osc_data[chip_data->ring_osc].quot);
989
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +0530990 /* Initialze the Debugfs Entry for cpr */
991 res = msm_cpr_debug_init(cpr->base);
992 if (res) {
993 pr_err("CPR: Debugfs Creation Failed\n");
994 goto err_ioremap;
995 }
996
997 /* Register the interrupt handler for IRQ 0 */
998 res = request_threaded_irq(irqn, NULL, cpr_irq0_handler,
999 IRQF_TRIGGER_RISING, "msm-cpr-irq0", cpr);
1000 if (res) {
1001 pr_err("CPR: request irq failed for IRQ %d\n", irqn);
1002 goto err_ioremap;
1003 }
1004
1005 /**
1006 * Enable the requested interrupt lines.
1007 * Do not enable MID_INT since we shall use
1008 * SW_AUTO_CONT_ACK_EN bit.
1009 */
1010 irq_enabled = INT_MASK & ~MID_INT;
1011 cpr_modify_reg(cpr, RBIF_IRQ_EN(cpr->config->irq_line),
1012 INT_MASK, irq_enabled);
1013
1014 /* Enable the cpr */
1015 cpr_modify_reg(cpr, RBCPR_CTL, LOOP_EN_M, ENABLE_CPR);
1016
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +05301017 cpr->freq_transition.notifier_call = cpr_freq_transition;
1018 cpufreq_register_notifier(&cpr->freq_transition,
1019 CPUFREQ_TRANSITION_NOTIFIER);
1020
Kaushal Kumarc958cecd2012-09-25 08:44:28 +05301021 pr_info("MSM CPR driver successfully registered!\n");
1022
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +05301023 return res;
1024
1025err_reg_get:
1026 free_irq(irqn, cpr);
1027err_ioremap:
1028 iounmap(base);
1029out:
Trilok Soni243960a2012-09-23 00:29:37 +05301030 enable = false;
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +05301031 return res;
1032}
1033
1034static int __devexit msm_cpr_remove(struct platform_device *pdev)
1035{
1036 struct msm_cpr *cpr = platform_get_drvdata(pdev);
1037
1038 cpufreq_unregister_notifier(&cpr->freq_transition,
1039 CPUFREQ_TRANSITION_NOTIFIER);
1040
1041 regulator_disable(cpr->vreg_cx);
1042 regulator_put(cpr->vreg_cx);
1043 free_irq(cpr->irq, cpr);
1044 iounmap(cpr->base);
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +05301045 platform_set_drvdata(pdev, NULL);
1046
1047 return 0;
1048}
1049
1050static const struct dev_pm_ops msm_cpr_dev_pm_ops = {
1051 .suspend = msm_cpr_suspend,
1052 .resume = msm_cpr_resume,
1053};
1054
1055static struct platform_driver msm_cpr_driver = {
1056 .probe = msm_cpr_probe,
1057 .remove = __devexit_p(msm_cpr_remove),
1058 .driver = {
1059 .name = MODULE_NAME,
1060 .owner = THIS_MODULE,
1061#ifdef CONFIG_PM
1062 .pm = &msm_cpr_dev_pm_ops,
1063#endif
1064 },
1065};
1066
1067static int __init msm_init_cpr(void)
1068{
1069 return platform_driver_register(&msm_cpr_driver);
1070}
1071
1072module_init(msm_init_cpr);
1073
1074static void __exit msm_exit_cpr(void)
1075{
1076 platform_driver_unregister(&msm_cpr_driver);
1077}
1078
1079module_exit(msm_exit_cpr);
1080
1081MODULE_DESCRIPTION("MSM CPR Driver");
1082MODULE_VERSION("1.0");
1083MODULE_LICENSE("GPL v2");