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