aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-ux500/regulator.c
blob: 97f41f263b116f37703ec4e499f4853726c60cac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*
 * Copyright (C) ST-Ericsson SA 2010
 *
 * License Terms: GNU General Public License v2
 * Author: Sundar Iyer <sundar.iyer@stericsson.com>
 *
 * DB8500 specific regulators on AB8500
 *
 * Supplies on the AB8500 which are dedicated to AB8500 and AB8500 devices
 * are included here as machine specific -
 * VARM, VMODEM, VAPE, VMSPS3(VSAFE), VPLL, VANA, VBBN/VBBP
 *
 * following points must be noted :
 *  - VARM,VMODEM and VSAFE wont be accountable to the framework.
 *  - VAPE cannot be turned off as it is the interconnect supply.
 *
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/consumer.h>

#include <mach/ab8500.h>
#include <mach/prcmu-fw-api.h>

#define U8500_NUM_REGULATORS	(5)

#define DB8500_DCDC_VAPE        (0)
#define DB8500_LDO_VANA         (1)

#define REGULATOR_ENABLED       (1)
#define REGULATOR_DISABLED      (0)

uint32_t vape_mode = REGULATOR_MODE_NORMAL;

struct regulator_priv {
	unsigned int status;
	unsigned int operating_point;
	unsigned int opp_mode_dep_count;
};

static int dcdc_vape_get_voltage(struct regulator_dev *rdev)
{
	/* returning a dummy non-zero to support optimum_mode*
	 * helpers
	 */
	return 1;
}

static int dcdc_vape_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
	int regulator_id;
	struct regulator_priv *priv = rdev_get_drvdata(rdev);

	regulator_id = rdev_get_id(rdev);
	if (regulator_id >= U8500_NUM_REGULATORS)
		return -EINVAL;

	dev_dbg(rdev_get_dev(rdev), "set_mode = %d\n", mode);

	/*
	 * we map here the various mode as
	 * REGULATOR_MODE_IDLE - AP 50OPP
	 * REGULATOR_MODE_NORMAL - AP 100OPP
	 *
	 */
	switch (mode) {
	case REGULATOR_MODE_IDLE:
		if (priv->operating_point == APE_50_OPP)
			return 0;
		/* request PRCMU for a transition to 50OPP */
		if (prcmu_set_ape_opp(APE_50_OPP)) {
			dev_dbg(rdev_get_dev(rdev),
					"APE 50OPP DVFS failed\n");
			return -EINVAL;
		}
		priv->operating_point = APE_50_OPP;
		break;
	case REGULATOR_MODE_NORMAL:
		if (priv->operating_point == APE_100_OPP)
			return 0;
		/* request PRCMU for a transition to 50OPP */
		if (prcmu_set_ape_opp(APE_100_OPP)) {
			dev_dbg(rdev_get_dev(rdev),
					"APE 100OPP DVFS failed\n");
			return -EINVAL;
		}
		priv->operating_point = APE_100_OPP;
		break;
	default:
		break;
	}

	return 0;
}

static int dcdc_vape_get_mode(struct regulator_dev *rdev)
{
	dev_dbg(rdev_get_dev(rdev),
			"get_mode returning %d\n", vape_mode);

	return vape_mode;

}

static unsigned int dcdc_vape_get_optimum_mode(struct regulator_dev *rdev,
		int input_uV, int output_uV, int load_uA)
{
	int regulator_id;
	struct regulator_priv *priv = rdev_get_drvdata(rdev);

	regulator_id = rdev_get_id(rdev);
	if (regulator_id >= U8500_NUM_REGULATORS)
		return -EINVAL;

	dev_dbg(rdev_get_dev(rdev), "get_optimum_mode: \
			input_uV=%d output_uV=%d load_uA=%d\n",
			input_uV, output_uV, load_uA);

	/* load_uA will be non-zero if any non-dvfs client is active */
	if (load_uA)
		priv->opp_mode_dep_count++;
	else
		priv->opp_mode_dep_count--;

	if (priv->opp_mode_dep_count) {
		return REGULATOR_MODE_NORMAL;
	} else {
		return REGULATOR_MODE_IDLE;
	}
}

static int dcdc_vape_enable(struct regulator_dev *rdev)
{
	int regulator_id;
	struct regulator_priv *priv = rdev_get_drvdata(rdev);

	regulator_id = rdev_get_id(rdev);
	if (regulator_id >= U8500_NUM_REGULATORS)
		return -EINVAL;

	priv->status = REGULATOR_ENABLED;

	return 0;
}

static int dcdc_vape_disable(struct regulator_dev *rdev)
{
	int regulator_id;
	struct regulator_priv *priv = rdev_get_drvdata(rdev);

	regulator_id = rdev_get_id(rdev);
	if (regulator_id >= U8500_NUM_REGULATORS)
		return -EINVAL;

	priv->status = REGULATOR_DISABLED;

	return 0;

}

static int dcdc_vape_is_enabled(struct regulator_dev *rdev)
{
	int regulator_id;
	struct regulator_priv *priv = rdev_get_drvdata(rdev);

	regulator_id = rdev_get_id(rdev);
	if (regulator_id >= U8500_NUM_REGULATORS)
		return -EINVAL;

	return priv->status;
}

static int ldo_vana_enable(struct regulator_dev *rdev)
{
	int regulator_id;

	regulator_id = rdev_get_id(rdev);
	if (regulator_id >= U8500_NUM_REGULATORS)
		return -EINVAL;
	prcmu_i2c_write(AB8500_REGU_CTRL2,
			AB8500_REGU_VPLLVANA_REGU_REG, 0x05);
	return 0;
};

static int ldo_vana_disable(struct regulator_dev *rdev)
{
	int regulator_id;

	regulator_id = rdev_get_id(rdev);
	if (regulator_id >= U8500_NUM_REGULATORS)
		return -EINVAL;

	return 0;
}

static struct regulator_ops dcdc_vape_ops = {
	.get_voltage            = dcdc_vape_get_voltage,
	.set_mode		= dcdc_vape_set_mode,
	.get_mode		= dcdc_vape_get_mode,
	.get_optimum_mode	= dcdc_vape_get_optimum_mode,
	.enable			= dcdc_vape_enable,
	.disable		= dcdc_vape_disable,
	.is_enabled		= dcdc_vape_is_enabled,
};

static struct regulator_ops ldo_vana_ops = {
	.enable                 = ldo_vana_enable,
	.disable                = ldo_vana_disable,
};

static struct regulator_desc db8500_desc[U8500_NUM_REGULATORS] = {
	{
		.name  	= "DCDC-VAPE",
		.id	= DB8500_DCDC_VAPE,
		.ops   	= &dcdc_vape_ops,
		.type  	= REGULATOR_VOLTAGE,
		.owner 	= THIS_MODULE,
	},
	{
		.name   = "LDO-VANA",
		.id     = DB8500_LDO_VANA,
		.ops    = &ldo_vana_ops,
		.type   = REGULATOR_VOLTAGE,
		.owner  = THIS_MODULE,
	},
};

static int __devinit db8500_regulator_probe(struct platform_device *pdev)
{
	struct regulator_dev *rdev;
	struct regulator_priv *priv;
	struct regulator_init_data *init_data = pdev->dev.platform_data;

	priv = kzalloc(sizeof(struct regulator_priv), GFP_KERNEL);
	if (!priv) {
		dev_dbg(&pdev->dev, "couldn't alloctae priv!!\n");
		return -EINVAL;
	}

	priv->status = 0;
	priv->operating_point = 0;
	priv->opp_mode_dep_count = 0;

	/* TODO : pass the regulator specific data to register */
	rdev = regulator_register(&db8500_desc[pdev->id], &pdev->dev,
							init_data, priv);
	if (IS_ERR(rdev)) {
		dev_dbg(&pdev->dev, "couldn't register regulator\n");
		return PTR_ERR(rdev);
	}

	platform_set_drvdata(pdev, rdev);

	return 0;
}

static int __devexit db8500_regulator_remove(struct platform_device *pdev)
{
	struct regulator_dev *reg_dev = platform_get_drvdata(pdev);

	regulator_unregister(reg_dev);

	return 0;
}

static struct platform_driver db8500_regulator_driver = {
	.driver = {
		.name = "db8500-regulator",
	},
	.probe	= db8500_regulator_probe,
	.remove = __devexit_p(db8500_regulator_remove),
};

static int __init db8500_regulator_init(void)
{
	int ret = 0;

	ret = platform_driver_register(&db8500_regulator_driver);
	if (ret) {
		printk(KERN_INFO "db8500_regulator : platform_driver_register fails\n");
		return -ENODEV;
	}

	return 0;
}

static void __exit db8500_regulator_exit(void)
{
	platform_driver_unregister(&db8500_regulator_driver);
}

/* replacing subsys_call as we want amba devices to be "powered" on */
arch_initcall(db8500_regulator_init);
module_exit(db8500_regulator_exit);

MODULE_AUTHOR("STMicroelectronics/ST-Ericsson");
MODULE_DESCRIPTION("DB8500 voltage framework");
MODULE_LICENSE("GPL");