linux/drivers/regulator/virtual.c
<<
>>
Prefs
   1/*
   2 * reg-virtual-consumer.c
   3 *
   4 * Copyright 2008 Wolfson Microelectronics PLC.
   5 *
   6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 of the
  11 * License, or (at your option) any later version.
  12 */
  13
  14#include <linux/err.h>
  15#include <linux/mutex.h>
  16#include <linux/platform_device.h>
  17#include <linux/regulator/consumer.h>
  18#include <linux/slab.h>
  19#include <linux/module.h>
  20
  21struct virtual_consumer_data {
  22        struct mutex lock;
  23        struct regulator *regulator;
  24        bool enabled;
  25        int min_uV;
  26        int max_uV;
  27        int min_uA;
  28        int max_uA;
  29        unsigned int mode;
  30};
  31
  32static void update_voltage_constraints(struct device *dev,
  33                                       struct virtual_consumer_data *data)
  34{
  35        int ret;
  36
  37        if (data->min_uV && data->max_uV
  38            && data->min_uV <= data->max_uV) {
  39                dev_dbg(dev, "Requesting %d-%duV\n",
  40                        data->min_uV, data->max_uV);
  41                ret = regulator_set_voltage(data->regulator,
  42                                        data->min_uV, data->max_uV);
  43                if (ret != 0) {
  44                        dev_err(dev,
  45                                "regulator_set_voltage() failed: %d\n", ret);
  46                        return;
  47                }
  48        }
  49
  50        if (data->min_uV && data->max_uV && !data->enabled) {
  51                dev_dbg(dev, "Enabling regulator\n");
  52                ret = regulator_enable(data->regulator);
  53                if (ret == 0)
  54                        data->enabled = true;
  55                else
  56                        dev_err(dev, "regulator_enable() failed: %d\n",
  57                                ret);
  58        }
  59
  60        if (!(data->min_uV && data->max_uV) && data->enabled) {
  61                dev_dbg(dev, "Disabling regulator\n");
  62                ret = regulator_disable(data->regulator);
  63                if (ret == 0)
  64                        data->enabled = false;
  65                else
  66                        dev_err(dev, "regulator_disable() failed: %d\n",
  67                                ret);
  68        }
  69}
  70
  71static void update_current_limit_constraints(struct device *dev,
  72                                          struct virtual_consumer_data *data)
  73{
  74        int ret;
  75
  76        if (data->max_uA
  77            && data->min_uA <= data->max_uA) {
  78                dev_dbg(dev, "Requesting %d-%duA\n",
  79                        data->min_uA, data->max_uA);
  80                ret = regulator_set_current_limit(data->regulator,
  81                                        data->min_uA, data->max_uA);
  82                if (ret != 0) {
  83                        dev_err(dev,
  84                                "regulator_set_current_limit() failed: %d\n",
  85                                ret);
  86                        return;
  87                }
  88        }
  89
  90        if (data->max_uA && !data->enabled) {
  91                dev_dbg(dev, "Enabling regulator\n");
  92                ret = regulator_enable(data->regulator);
  93                if (ret == 0)
  94                        data->enabled = true;
  95                else
  96                        dev_err(dev, "regulator_enable() failed: %d\n",
  97                                ret);
  98        }
  99
 100        if (!(data->min_uA && data->max_uA) && data->enabled) {
 101                dev_dbg(dev, "Disabling regulator\n");
 102                ret = regulator_disable(data->regulator);
 103                if (ret == 0)
 104                        data->enabled = false;
 105                else
 106                        dev_err(dev, "regulator_disable() failed: %d\n",
 107                                ret);
 108        }
 109}
 110
 111static ssize_t show_min_uV(struct device *dev,
 112                           struct device_attribute *attr, char *buf)
 113{
 114        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 115        return sprintf(buf, "%d\n", data->min_uV);
 116}
 117
 118static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr,
 119                          const char *buf, size_t count)
 120{
 121        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 122        long val;
 123
 124        if (kstrtol(buf, 10, &val) != 0)
 125                return count;
 126
 127        mutex_lock(&data->lock);
 128
 129        data->min_uV = val;
 130        update_voltage_constraints(dev, data);
 131
 132        mutex_unlock(&data->lock);
 133
 134        return count;
 135}
 136
 137static ssize_t show_max_uV(struct device *dev,
 138                           struct device_attribute *attr, char *buf)
 139{
 140        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 141        return sprintf(buf, "%d\n", data->max_uV);
 142}
 143
 144static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr,
 145                          const char *buf, size_t count)
 146{
 147        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 148        long val;
 149
 150        if (kstrtol(buf, 10, &val) != 0)
 151                return count;
 152
 153        mutex_lock(&data->lock);
 154
 155        data->max_uV = val;
 156        update_voltage_constraints(dev, data);
 157
 158        mutex_unlock(&data->lock);
 159
 160        return count;
 161}
 162
 163static ssize_t show_min_uA(struct device *dev,
 164                           struct device_attribute *attr, char *buf)
 165{
 166        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 167        return sprintf(buf, "%d\n", data->min_uA);
 168}
 169
 170static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr,
 171                          const char *buf, size_t count)
 172{
 173        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 174        long val;
 175
 176        if (kstrtol(buf, 10, &val) != 0)
 177                return count;
 178
 179        mutex_lock(&data->lock);
 180
 181        data->min_uA = val;
 182        update_current_limit_constraints(dev, data);
 183
 184        mutex_unlock(&data->lock);
 185
 186        return count;
 187}
 188
 189static ssize_t show_max_uA(struct device *dev,
 190                           struct device_attribute *attr, char *buf)
 191{
 192        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 193        return sprintf(buf, "%d\n", data->max_uA);
 194}
 195
 196static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr,
 197                          const char *buf, size_t count)
 198{
 199        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 200        long val;
 201
 202        if (kstrtol(buf, 10, &val) != 0)
 203                return count;
 204
 205        mutex_lock(&data->lock);
 206
 207        data->max_uA = val;
 208        update_current_limit_constraints(dev, data);
 209
 210        mutex_unlock(&data->lock);
 211
 212        return count;
 213}
 214
 215static ssize_t show_mode(struct device *dev,
 216                         struct device_attribute *attr, char *buf)
 217{
 218        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 219
 220        switch (data->mode) {
 221        case REGULATOR_MODE_FAST:
 222                return sprintf(buf, "fast\n");
 223        case REGULATOR_MODE_NORMAL:
 224                return sprintf(buf, "normal\n");
 225        case REGULATOR_MODE_IDLE:
 226                return sprintf(buf, "idle\n");
 227        case REGULATOR_MODE_STANDBY:
 228                return sprintf(buf, "standby\n");
 229        default:
 230                return sprintf(buf, "unknown\n");
 231        }
 232}
 233
 234static ssize_t set_mode(struct device *dev, struct device_attribute *attr,
 235                        const char *buf, size_t count)
 236{
 237        struct virtual_consumer_data *data = dev_get_drvdata(dev);
 238        unsigned int mode;
 239        int ret;
 240
 241        /*
 242         * sysfs_streq() doesn't need the \n's, but we add them so the strings
 243         * will be shared with show_mode(), above.
 244         */
 245        if (sysfs_streq(buf, "fast\n"))
 246                mode = REGULATOR_MODE_FAST;
 247        else if (sysfs_streq(buf, "normal\n"))
 248                mode = REGULATOR_MODE_NORMAL;
 249        else if (sysfs_streq(buf, "idle\n"))
 250                mode = REGULATOR_MODE_IDLE;
 251        else if (sysfs_streq(buf, "standby\n"))
 252                mode = REGULATOR_MODE_STANDBY;
 253        else {
 254                dev_err(dev, "Configuring invalid mode\n");
 255                return count;
 256        }
 257
 258        mutex_lock(&data->lock);
 259        ret = regulator_set_mode(data->regulator, mode);
 260        if (ret == 0)
 261                data->mode = mode;
 262        else
 263                dev_err(dev, "Failed to configure mode: %d\n", ret);
 264        mutex_unlock(&data->lock);
 265
 266        return count;
 267}
 268
 269static DEVICE_ATTR(min_microvolts, 0664, show_min_uV, set_min_uV);
 270static DEVICE_ATTR(max_microvolts, 0664, show_max_uV, set_max_uV);
 271static DEVICE_ATTR(min_microamps, 0664, show_min_uA, set_min_uA);
 272static DEVICE_ATTR(max_microamps, 0664, show_max_uA, set_max_uA);
 273static DEVICE_ATTR(mode, 0664, show_mode, set_mode);
 274
 275static struct attribute *regulator_virtual_attributes[] = {
 276        &dev_attr_min_microvolts.attr,
 277        &dev_attr_max_microvolts.attr,
 278        &dev_attr_min_microamps.attr,
 279        &dev_attr_max_microamps.attr,
 280        &dev_attr_mode.attr,
 281        NULL
 282};
 283
 284static const struct attribute_group regulator_virtual_attr_group = {
 285        .attrs  = regulator_virtual_attributes,
 286};
 287
 288static int regulator_virtual_probe(struct platform_device *pdev)
 289{
 290        char *reg_id = dev_get_platdata(&pdev->dev);
 291        struct virtual_consumer_data *drvdata;
 292        int ret;
 293
 294        drvdata = devm_kzalloc(&pdev->dev, sizeof(struct virtual_consumer_data),
 295                               GFP_KERNEL);
 296        if (drvdata == NULL)
 297                return -ENOMEM;
 298
 299        mutex_init(&drvdata->lock);
 300
 301        drvdata->regulator = devm_regulator_get(&pdev->dev, reg_id);
 302        if (IS_ERR(drvdata->regulator)) {
 303                ret = PTR_ERR(drvdata->regulator);
 304                dev_err(&pdev->dev, "Failed to obtain supply '%s': %d\n",
 305                        reg_id, ret);
 306                return ret;
 307        }
 308
 309        ret = sysfs_create_group(&pdev->dev.kobj,
 310                                 &regulator_virtual_attr_group);
 311        if (ret != 0) {
 312                dev_err(&pdev->dev,
 313                        "Failed to create attribute group: %d\n", ret);
 314                return ret;
 315        }
 316
 317        drvdata->mode = regulator_get_mode(drvdata->regulator);
 318
 319        platform_set_drvdata(pdev, drvdata);
 320
 321        return 0;
 322}
 323
 324static int regulator_virtual_remove(struct platform_device *pdev)
 325{
 326        struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev);
 327
 328        sysfs_remove_group(&pdev->dev.kobj, &regulator_virtual_attr_group);
 329
 330        if (drvdata->enabled)
 331                regulator_disable(drvdata->regulator);
 332
 333        return 0;
 334}
 335
 336static struct platform_driver regulator_virtual_consumer_driver = {
 337        .probe          = regulator_virtual_probe,
 338        .remove         = regulator_virtual_remove,
 339        .driver         = {
 340                .name           = "reg-virt-consumer",
 341        },
 342};
 343
 344module_platform_driver(regulator_virtual_consumer_driver);
 345
 346MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 347MODULE_DESCRIPTION("Virtual regulator consumer");
 348MODULE_LICENSE("GPL");
 349MODULE_ALIAS("platform:reg-virt-consumer");
 350