linux/drivers/regulator/userspace-consumer.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * userspace-consumer.c
   4 *
   5 * Copyright 2009 CompuLab, Ltd.
   6 *
   7 * Author: Mike Rapoport <mike@compulab.co.il>
   8 *
   9 * Based of virtual consumer driver:
  10 *   Copyright 2008 Wolfson Microelectronics PLC.
  11 *   Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  12 */
  13
  14#include <linux/err.h>
  15#include <linux/mutex.h>
  16#include <linux/module.h>
  17#include <linux/platform_device.h>
  18#include <linux/regulator/consumer.h>
  19#include <linux/regulator/userspace-consumer.h>
  20#include <linux/slab.h>
  21
  22struct userspace_consumer_data {
  23        const char *name;
  24
  25        struct mutex lock;
  26        bool enabled;
  27
  28        int num_supplies;
  29        struct regulator_bulk_data *supplies;
  30};
  31
  32static ssize_t name_show(struct device *dev,
  33                         struct device_attribute *attr, char *buf)
  34{
  35        struct userspace_consumer_data *data = dev_get_drvdata(dev);
  36
  37        return sprintf(buf, "%s\n", data->name);
  38}
  39
  40static ssize_t state_show(struct device *dev,
  41                          struct device_attribute *attr, char *buf)
  42{
  43        struct userspace_consumer_data *data = dev_get_drvdata(dev);
  44
  45        if (data->enabled)
  46                return sprintf(buf, "enabled\n");
  47
  48        return sprintf(buf, "disabled\n");
  49}
  50
  51static ssize_t state_store(struct device *dev, struct device_attribute *attr,
  52                           const char *buf, size_t count)
  53{
  54        struct userspace_consumer_data *data = dev_get_drvdata(dev);
  55        bool enabled;
  56        int ret;
  57
  58        /*
  59         * sysfs_streq() doesn't need the \n's, but we add them so the strings
  60         * will be shared with show_state(), above.
  61         */
  62        if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1"))
  63                enabled = true;
  64        else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0"))
  65                enabled = false;
  66        else {
  67                dev_err(dev, "Configuring invalid mode\n");
  68                return count;
  69        }
  70
  71        mutex_lock(&data->lock);
  72        if (enabled != data->enabled) {
  73                if (enabled)
  74                        ret = regulator_bulk_enable(data->num_supplies,
  75                                                    data->supplies);
  76                else
  77                        ret = regulator_bulk_disable(data->num_supplies,
  78                                                     data->supplies);
  79
  80                if (ret == 0)
  81                        data->enabled = enabled;
  82                else
  83                        dev_err(dev, "Failed to configure state: %d\n", ret);
  84        }
  85        mutex_unlock(&data->lock);
  86
  87        return count;
  88}
  89
  90static DEVICE_ATTR_RO(name);
  91static DEVICE_ATTR_RW(state);
  92
  93static struct attribute *attributes[] = {
  94        &dev_attr_name.attr,
  95        &dev_attr_state.attr,
  96        NULL,
  97};
  98
  99static const struct attribute_group attr_group = {
 100        .attrs  = attributes,
 101};
 102
 103static int regulator_userspace_consumer_probe(struct platform_device *pdev)
 104{
 105        struct regulator_userspace_consumer_data *pdata;
 106        struct userspace_consumer_data *drvdata;
 107        int ret;
 108
 109        pdata = dev_get_platdata(&pdev->dev);
 110        if (!pdata)
 111                return -EINVAL;
 112
 113        drvdata = devm_kzalloc(&pdev->dev,
 114                               sizeof(struct userspace_consumer_data),
 115                               GFP_KERNEL);
 116        if (drvdata == NULL)
 117                return -ENOMEM;
 118
 119        drvdata->name = pdata->name;
 120        drvdata->num_supplies = pdata->num_supplies;
 121        drvdata->supplies = pdata->supplies;
 122
 123        mutex_init(&drvdata->lock);
 124
 125        ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies,
 126                                      drvdata->supplies);
 127        if (ret) {
 128                dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret);
 129                return ret;
 130        }
 131
 132        ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
 133        if (ret != 0)
 134                return ret;
 135
 136        if (pdata->init_on) {
 137                ret = regulator_bulk_enable(drvdata->num_supplies,
 138                                            drvdata->supplies);
 139                if (ret) {
 140                        dev_err(&pdev->dev,
 141                                "Failed to set initial state: %d\n", ret);
 142                        goto err_enable;
 143                }
 144        }
 145
 146        drvdata->enabled = pdata->init_on;
 147        platform_set_drvdata(pdev, drvdata);
 148
 149        return 0;
 150
 151err_enable:
 152        sysfs_remove_group(&pdev->dev.kobj, &attr_group);
 153
 154        return ret;
 155}
 156
 157static int regulator_userspace_consumer_remove(struct platform_device *pdev)
 158{
 159        struct userspace_consumer_data *data = platform_get_drvdata(pdev);
 160
 161        sysfs_remove_group(&pdev->dev.kobj, &attr_group);
 162
 163        if (data->enabled)
 164                regulator_bulk_disable(data->num_supplies, data->supplies);
 165
 166        return 0;
 167}
 168
 169static struct platform_driver regulator_userspace_consumer_driver = {
 170        .probe          = regulator_userspace_consumer_probe,
 171        .remove         = regulator_userspace_consumer_remove,
 172        .driver         = {
 173                .name           = "reg-userspace-consumer",
 174        },
 175};
 176
 177module_platform_driver(regulator_userspace_consumer_driver);
 178
 179MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
 180MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
 181MODULE_LICENSE("GPL");
 182