blob: 47748c83b24e5d780faff3383dbd59fea0b1126f [file] [log] [blame]
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +05301/*
Duy Truonge833aca2013-02-12 13:35:08 -08002 * Copyright (c) 2012, The Linux Foundation. All rights reserved.
Pankaj Kumar32ce1ea2012-04-04 20:29:29 +05303 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#define pr_fmt(fmt) "%s: " fmt, __func__
16
17#include <linux/module.h>
18#include <linux/io.h>
19#include <linux/err.h>
20#include <linux/init.h>
21#include <linux/slab.h>
22#include <linux/delay.h>
23#include <linux/errno.h>
24#include <linux/platform_device.h>
25#include <linux/regulator/driver.h>
26#include <linux/regulator/machine.h>
27
28#include <mach/msm_iomap.h>
29
30/* Address for Perf Level Registor */
31#define VDD_APC_PLEVEL_BASE (MSM_CLK_CTL_BASE + 0x0298)
32#define VDD_APC_PLEVEL(n) (VDD_APC_PLEVEL_BASE + 4 * n)
33
34/* Address for SYS_P_Level register */
35#define VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124)
36
37#define MV_TO_UV(mv) ((mv)*1000)
38#define UV_TO_MV(uv) (((uv)+999)/1000)
39
40#define MSM_VP_REGULATOR_DEV_NAME "vp-regulator"
41
42/**
43 * Convert Voltage to PLEVEL register value
44 * Here x is required voltage in minivolt
45 * e.g. if Required voltage is 1200mV then
46 * required value to be programmed into the
47 * Plevel register is 0x32. This equation is
48 * based on H/W logic being used in SVS controller.
49 *
50 * Here we are taking the minimum voltage step
51 * to be 12.5mV as per H/W logic and adding 0x20
52 * is for selecting the reference voltage.
53 * 750mV is minimum voltage of MSMC2 smps.
54 */
55#define VOLT_TO_BIT(x) (((x-750)/(12500/1000)) + 0x20)
56#define VREG_VREF_SEL (1 << 5)
57#define VREG_PD_EN (1 << 6)
58
59/**
60 * struct msm_vp - Structure for VP
61 * @regulator_dev: structure for regulator device
62 * @current_voltage: current voltage value
63 */
64struct msm_vp {
65 struct device *dev;
66 struct regulator_dev *rdev;
67 int current_voltage;
68};
69
70/* Function to change the Vdd Level */
71static int vp_reg_set_voltage(struct regulator_dev *rdev, int min_uV,
72 int max_uV, unsigned *sel)
73{
74 struct msm_vp *vp = rdev_get_drvdata(rdev);
75 uint32_t reg_val, perf_level, plevel, cur_plevel, fine_step_volt;
76
77 reg_val = readl_relaxed(VDD_SVS_PLEVEL_ADDR);
78 perf_level = reg_val & 0x07;
79
80 plevel = (min_uV - 750000) / 25000;
81 fine_step_volt = (min_uV - 750000) % 25000;
82
83 /**
84 * Program the new voltage level for the current perf_level
85 * in corresponding PLEVEL register.
86 */
87 cur_plevel = readl_relaxed(VDD_APC_PLEVEL(perf_level));
88 /* clear lower 6 bits */
89 cur_plevel &= ~0x3F;
90 cur_plevel |= (plevel | VREG_VREF_SEL);
91 if (fine_step_volt >= 12500)
92 cur_plevel |= VREG_PD_EN;
93 writel_relaxed(cur_plevel, VDD_APC_PLEVEL(perf_level));
94
95 /* Clear the current perf level */
96 reg_val &= 0xF8;
97 writel_relaxed(reg_val, VDD_SVS_PLEVEL_ADDR);
98
99 /* Initiate the PMIC SSBI request to change the voltage */
100 reg_val |= (BIT(7) | perf_level << 3);
101 writel_relaxed(reg_val, VDD_SVS_PLEVEL_ADDR);
102 mb();
103 udelay(62);
104
105 if ((readl_relaxed(VDD_SVS_PLEVEL_ADDR) & 0x07) != perf_level) {
106 pr_err("Vdd Set Failed\n");
107 return -EIO;
108 }
109
110 vp->current_voltage = (min_uV / 1000);
111 return 0;
112}
113
114static int vp_reg_get_voltage(struct regulator_dev *rdev)
115{
116 struct msm_vp *vp = rdev_get_drvdata(rdev);
117
118 return MV_TO_UV(vp->current_voltage);
119}
120
121static int vp_reg_enable(struct regulator_dev *rdev)
122{
123 return 0;
124}
125
126static int vp_reg_disable(struct regulator_dev *rdev)
127{
128 return 0;
129}
130
131/* Regulator registration specific data */
132/* FIXME: should move to board-xx-regulator.c file */
133static struct regulator_consumer_supply vp_consumer =
134 REGULATOR_SUPPLY("vddx_cx", "msm-cpr");
135
136static struct regulator_init_data vp_reg_data = {
137 .constraints = {
138 .name = "vddx_c2",
139 .min_uV = 750000,
140 .max_uV = 1500000,
141 .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
142 REGULATOR_CHANGE_STATUS,
143 .valid_modes_mask = REGULATOR_MODE_NORMAL,
144 .boot_on = 1,
145 .input_uV = 0,
146 .always_on = 1,
147 },
148 .num_consumer_supplies = 1,
149 .consumer_supplies = &vp_consumer,
150};
151
152/* Regulator specific ops */
153static struct regulator_ops vp_reg_ops = {
154 .enable = vp_reg_enable,
155 .disable = vp_reg_disable,
156 .get_voltage = vp_reg_get_voltage,
157 .set_voltage = vp_reg_set_voltage,
158};
159
160/* Regulator Description */
161static struct regulator_desc vp_reg = {
162 .name = "vddcx",
163 .id = -1,
164 .ops = &vp_reg_ops,
165 .type = REGULATOR_VOLTAGE,
166};
167
168static int __devinit msm_vp_reg_probe(struct platform_device *pdev)
169{
170 struct msm_vp *vp;
171 int rc;
172
173 vp = kzalloc(sizeof(struct msm_vp), GFP_KERNEL);
174 if (!vp) {
175 pr_err("Could not allocate memory for VP\n");
176 return -ENOMEM;
177 }
178
179 vp->rdev = regulator_register(&vp_reg, NULL, &vp_reg_data, vp, NULL);
180 if (IS_ERR(vp->rdev)) {
181 rc = PTR_ERR(vp->rdev);
182 pr_err("Failed to register regulator: %d\n", rc);
183 goto error;
184 }
185
186 platform_set_drvdata(pdev, vp);
187
188 return 0;
189error:
190 kfree(vp);
191 return rc;
192}
193
194static int __devexit msm_vp_reg_remove(struct platform_device *pdev)
195{
196 struct msm_vp *vp = platform_get_drvdata(pdev);
197
198 regulator_unregister(vp->rdev);
199 platform_set_drvdata(pdev, NULL);
200 kfree(vp);
201
202 return 0;
203}
204
205static struct platform_driver msm_vp_reg_driver = {
206 .probe = msm_vp_reg_probe,
207 .remove = __devexit_p(msm_vp_reg_remove),
208 .driver = {
209 .name = MSM_VP_REGULATOR_DEV_NAME,
210 .owner = THIS_MODULE,
211 },
212};
213
214static int __init msm_vp_reg_init(void)
215{
216 return platform_driver_register(&msm_vp_reg_driver);
217}
218postcore_initcall(msm_vp_reg_init);
219
220static void __exit msm_vp_reg_exit(void)
221{
222 platform_driver_unregister(&msm_vp_reg_driver);
223}
224module_exit(msm_vp_reg_exit);
225
226MODULE_LICENSE("GPL v2");
227MODULE_DESCRIPTION("MSM VP regulator driver");
228MODULE_VERSION("1.0");
229MODULE_ALIAS("platform:" MSM_VP_REGULATOR_DEV_NAME);