virtual.c 8.4 KB
Newer Older
M
Mark Brown 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * reg-virtual-consumer.c
 *
 * Copyright 2008 Wolfson Microelectronics PLC.
 *
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 */

#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
18
#include <linux/slab.h>
19
#include <linux/module.h>
M
Mark Brown 已提交
20 21 22 23

struct virtual_consumer_data {
	struct mutex lock;
	struct regulator *regulator;
24
	bool enabled;
M
Mark Brown 已提交
25 26 27 28 29 30 31
	int min_uV;
	int max_uV;
	int min_uA;
	int max_uA;
	unsigned int mode;
};

32 33
static void update_voltage_constraints(struct device *dev,
				       struct virtual_consumer_data *data)
M
Mark Brown 已提交
34 35 36 37 38
{
	int ret;

	if (data->min_uV && data->max_uV
	    && data->min_uV <= data->max_uV) {
39 40
		dev_dbg(dev, "Requesting %d-%duV\n",
			data->min_uV, data->max_uV);
M
Mark Brown 已提交
41
		ret = regulator_set_voltage(data->regulator,
42
					data->min_uV, data->max_uV);
M
Mark Brown 已提交
43
		if (ret != 0) {
44 45
			dev_err(dev,
				"regulator_set_voltage() failed: %d\n", ret);
M
Mark Brown 已提交
46 47 48 49 50
			return;
		}
	}

	if (data->min_uV && data->max_uV && !data->enabled) {
51
		dev_dbg(dev, "Enabling regulator\n");
M
Mark Brown 已提交
52 53
		ret = regulator_enable(data->regulator);
		if (ret == 0)
54
			data->enabled = true;
M
Mark Brown 已提交
55
		else
56
			dev_err(dev, "regulator_enable() failed: %d\n",
M
Mark Brown 已提交
57 58 59 60
				ret);
	}

	if (!(data->min_uV && data->max_uV) && data->enabled) {
61
		dev_dbg(dev, "Disabling regulator\n");
M
Mark Brown 已提交
62 63
		ret = regulator_disable(data->regulator);
		if (ret == 0)
64
			data->enabled = false;
M
Mark Brown 已提交
65
		else
66
			dev_err(dev, "regulator_disable() failed: %d\n",
M
Mark Brown 已提交
67 68 69 70
				ret);
	}
}

71 72
static void update_current_limit_constraints(struct device *dev,
					  struct virtual_consumer_data *data)
M
Mark Brown 已提交
73 74 75 76 77
{
	int ret;

	if (data->max_uA
	    && data->min_uA <= data->max_uA) {
78 79
		dev_dbg(dev, "Requesting %d-%duA\n",
			data->min_uA, data->max_uA);
M
Mark Brown 已提交
80 81 82
		ret = regulator_set_current_limit(data->regulator,
					data->min_uA, data->max_uA);
		if (ret != 0) {
83 84 85
			dev_err(dev,
				"regulator_set_current_limit() failed: %d\n",
				ret);
M
Mark Brown 已提交
86 87 88 89 90
			return;
		}
	}

	if (data->max_uA && !data->enabled) {
91
		dev_dbg(dev, "Enabling regulator\n");
M
Mark Brown 已提交
92 93
		ret = regulator_enable(data->regulator);
		if (ret == 0)
94
			data->enabled = true;
M
Mark Brown 已提交
95
		else
96
			dev_err(dev, "regulator_enable() failed: %d\n",
M
Mark Brown 已提交
97 98 99 100
				ret);
	}

	if (!(data->min_uA && data->max_uA) && data->enabled) {
101
		dev_dbg(dev, "Disabling regulator\n");
M
Mark Brown 已提交
102 103
		ret = regulator_disable(data->regulator);
		if (ret == 0)
104
			data->enabled = false;
M
Mark Brown 已提交
105
		else
106
			dev_err(dev, "regulator_disable() failed: %d\n",
M
Mark Brown 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
				ret);
	}
}

static ssize_t show_min_uV(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", data->min_uV);
}

static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	long val;

124
	if (kstrtol(buf, 10, &val) != 0)
M
Mark Brown 已提交
125 126 127 128 129
		return count;

	mutex_lock(&data->lock);

	data->min_uV = val;
130
	update_voltage_constraints(dev, data);
M
Mark Brown 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

	mutex_unlock(&data->lock);

	return count;
}

static ssize_t show_max_uV(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", data->max_uV);
}

static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	long val;

150
	if (kstrtol(buf, 10, &val) != 0)
M
Mark Brown 已提交
151 152 153 154 155
		return count;

	mutex_lock(&data->lock);

	data->max_uV = val;
156
	update_voltage_constraints(dev, data);
M
Mark Brown 已提交
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175

	mutex_unlock(&data->lock);

	return count;
}

static ssize_t show_min_uA(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", data->min_uA);
}

static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	long val;

176
	if (kstrtol(buf, 10, &val) != 0)
M
Mark Brown 已提交
177 178 179 180 181
		return count;

	mutex_lock(&data->lock);

	data->min_uA = val;
182
	update_current_limit_constraints(dev, data);
M
Mark Brown 已提交
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

	mutex_unlock(&data->lock);

	return count;
}

static ssize_t show_max_uA(struct device *dev,
			   struct device_attribute *attr, char *buf)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	return sprintf(buf, "%d\n", data->max_uA);
}

static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr,
			  const char *buf, size_t count)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	long val;

202
	if (kstrtol(buf, 10, &val) != 0)
M
Mark Brown 已提交
203 204 205 206 207
		return count;

	mutex_lock(&data->lock);

	data->max_uA = val;
208
	update_current_limit_constraints(dev, data);
M
Mark Brown 已提交
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

	mutex_unlock(&data->lock);

	return count;
}

static ssize_t show_mode(struct device *dev,
			 struct device_attribute *attr, char *buf)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);

	switch (data->mode) {
	case REGULATOR_MODE_FAST:
		return sprintf(buf, "fast\n");
	case REGULATOR_MODE_NORMAL:
		return sprintf(buf, "normal\n");
	case REGULATOR_MODE_IDLE:
		return sprintf(buf, "idle\n");
	case REGULATOR_MODE_STANDBY:
		return sprintf(buf, "standby\n");
	default:
		return sprintf(buf, "unknown\n");
	}
}

static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
			const char *buf, size_t count)
{
	struct virtual_consumer_data *data = dev_get_drvdata(dev);
	unsigned int mode;
	int ret;

241 242 243 244
	/*
	 * sysfs_streq() doesn't need the \n's, but we add them so the strings
	 * will be shared with show_mode(), above.
	 */
245
	if (sysfs_streq(buf, "fast\n"))
M
Mark Brown 已提交
246
		mode = REGULATOR_MODE_FAST;
247
	else if (sysfs_streq(buf, "normal\n"))
M
Mark Brown 已提交
248
		mode = REGULATOR_MODE_NORMAL;
249
	else if (sysfs_streq(buf, "idle\n"))
M
Mark Brown 已提交
250
		mode = REGULATOR_MODE_IDLE;
251
	else if (sysfs_streq(buf, "standby\n"))
M
Mark Brown 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
		mode = REGULATOR_MODE_STANDBY;
	else {
		dev_err(dev, "Configuring invalid mode\n");
		return count;
	}

	mutex_lock(&data->lock);
	ret = regulator_set_mode(data->regulator, mode);
	if (ret == 0)
		data->mode = mode;
	else
		dev_err(dev, "Failed to configure mode: %d\n", ret);
	mutex_unlock(&data->lock);

	return count;
}

static DEVICE_ATTR(min_microvolts, 0666, show_min_uV, set_min_uV);
static DEVICE_ATTR(max_microvolts, 0666, show_max_uV, set_max_uV);
static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA);
static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA);
static DEVICE_ATTR(mode, 0666, show_mode, set_mode);

275 276 277 278 279 280 281
static struct attribute *regulator_virtual_attributes[] = {
	&dev_attr_min_microvolts.attr,
	&dev_attr_max_microvolts.attr,
	&dev_attr_min_microamps.attr,
	&dev_attr_max_microamps.attr,
	&dev_attr_mode.attr,
	NULL
M
Mark Brown 已提交
282 283
};

284 285 286 287
static const struct attribute_group regulator_virtual_attr_group = {
	.attrs	= regulator_virtual_attributes,
};

288
static int regulator_virtual_probe(struct platform_device *pdev)
M
Mark Brown 已提交
289 290 291
{
	char *reg_id = pdev->dev.platform_data;
	struct virtual_consumer_data *drvdata;
292
	int ret;
M
Mark Brown 已提交
293

294 295
	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data),
			       GFP_KERNEL);
296
	if (drvdata == NULL)
297
		return -ENOMEM;
M
Mark Brown 已提交
298 299 300

	mutex_init(&drvdata->lock);

301
	drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
M
Mark Brown 已提交
302 303
	if (IS_ERR(drvdata->regulator)) {
		ret = PTR_ERR(drvdata->regulator);
304 305
		dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n",
			reg_id, ret);
306
		return ret;
M
Mark Brown 已提交
307 308
	}

309 310 311 312 313
	ret = sysfs_create_group(&pdev->dev.kobj,
				 &regulator_virtual_attr_group);
	if (ret != 0) {
		dev_err(&pdev->dev,
			"Failed to create attribute group: %d\n", ret);
314
		return ret;
M
Mark Brown 已提交
315 316 317 318 319 320 321 322 323
	}

	drvdata->mode = regulator_get_mode(drvdata->regulator);

	platform_set_drvdata(pdev, drvdata);

	return 0;
}

324
static int regulator_virtual_remove(struct platform_device *pdev)
M
Mark Brown 已提交
325 326 327
{
	struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev);

328 329
	sysfs_remove_group(&pdev->dev.kobj, &regulator_virtual_attr_group);

M
Mark Brown 已提交
330 331 332 333 334 335 336
	if (drvdata->enabled)
		regulator_disable(drvdata->regulator);

	return 0;
}

static struct platform_driver regulator_virtual_consumer_driver = {
337
	.probe		= regulator_virtual_probe,
338
	.remove		= regulator_virtual_remove,
M
Mark Brown 已提交
339 340
	.driver		= {
		.name		= "reg-virt-consumer",
341
		.owner		= THIS_MODULE,
M
Mark Brown 已提交
342 343 344
	},
};

345
module_platform_driver(regulator_virtual_consumer_driver);
M
Mark Brown 已提交
346 347 348 349

MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_DESCRIPTION("Virtual regulator consumer");
MODULE_LICENSE("GPL");
350
MODULE_ALIAS("platform:reg-virt-consumer");