Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * userspace-consumer.c
0004  *
0005  * Copyright 2009 CompuLab, Ltd.
0006  *
0007  * Author: Mike Rapoport <mike@compulab.co.il>
0008  *
0009  * Based of virtual consumer driver:
0010  *   Copyright 2008 Wolfson Microelectronics PLC.
0011  *   Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
0012  */
0013 
0014 #include <linux/err.h>
0015 #include <linux/mutex.h>
0016 #include <linux/module.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/regulator/consumer.h>
0019 #include <linux/regulator/userspace-consumer.h>
0020 #include <linux/slab.h>
0021 
0022 struct userspace_consumer_data {
0023     const char *name;
0024 
0025     struct mutex lock;
0026     bool enabled;
0027 
0028     int num_supplies;
0029     struct regulator_bulk_data *supplies;
0030 };
0031 
0032 static ssize_t name_show(struct device *dev,
0033              struct device_attribute *attr, char *buf)
0034 {
0035     struct userspace_consumer_data *data = dev_get_drvdata(dev);
0036 
0037     return sprintf(buf, "%s\n", data->name);
0038 }
0039 
0040 static ssize_t state_show(struct device *dev,
0041               struct device_attribute *attr, char *buf)
0042 {
0043     struct userspace_consumer_data *data = dev_get_drvdata(dev);
0044 
0045     if (data->enabled)
0046         return sprintf(buf, "enabled\n");
0047 
0048     return sprintf(buf, "disabled\n");
0049 }
0050 
0051 static ssize_t state_store(struct device *dev, struct device_attribute *attr,
0052                const char *buf, size_t count)
0053 {
0054     struct userspace_consumer_data *data = dev_get_drvdata(dev);
0055     bool enabled;
0056     int ret;
0057 
0058     /*
0059      * sysfs_streq() doesn't need the \n's, but we add them so the strings
0060      * will be shared with show_state(), above.
0061      */
0062     if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1"))
0063         enabled = true;
0064     else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0"))
0065         enabled = false;
0066     else {
0067         dev_err(dev, "Configuring invalid mode\n");
0068         return count;
0069     }
0070 
0071     mutex_lock(&data->lock);
0072     if (enabled != data->enabled) {
0073         if (enabled)
0074             ret = regulator_bulk_enable(data->num_supplies,
0075                             data->supplies);
0076         else
0077             ret = regulator_bulk_disable(data->num_supplies,
0078                              data->supplies);
0079 
0080         if (ret == 0)
0081             data->enabled = enabled;
0082         else
0083             dev_err(dev, "Failed to configure state: %d\n", ret);
0084     }
0085     mutex_unlock(&data->lock);
0086 
0087     return count;
0088 }
0089 
0090 static DEVICE_ATTR_RO(name);
0091 static DEVICE_ATTR_RW(state);
0092 
0093 static struct attribute *attributes[] = {
0094     &dev_attr_name.attr,
0095     &dev_attr_state.attr,
0096     NULL,
0097 };
0098 
0099 static const struct attribute_group attr_group = {
0100     .attrs  = attributes,
0101 };
0102 
0103 static int regulator_userspace_consumer_probe(struct platform_device *pdev)
0104 {
0105     struct regulator_userspace_consumer_data *pdata;
0106     struct userspace_consumer_data *drvdata;
0107     int ret;
0108 
0109     pdata = dev_get_platdata(&pdev->dev);
0110     if (!pdata)
0111         return -EINVAL;
0112 
0113     drvdata = devm_kzalloc(&pdev->dev,
0114                    sizeof(struct userspace_consumer_data),
0115                    GFP_KERNEL);
0116     if (drvdata == NULL)
0117         return -ENOMEM;
0118 
0119     drvdata->name = pdata->name;
0120     drvdata->num_supplies = pdata->num_supplies;
0121     drvdata->supplies = pdata->supplies;
0122 
0123     mutex_init(&drvdata->lock);
0124 
0125     ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies,
0126                       drvdata->supplies);
0127     if (ret) {
0128         dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret);
0129         return ret;
0130     }
0131 
0132     ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
0133     if (ret != 0)
0134         return ret;
0135 
0136     if (pdata->init_on) {
0137         ret = regulator_bulk_enable(drvdata->num_supplies,
0138                         drvdata->supplies);
0139         if (ret) {
0140             dev_err(&pdev->dev,
0141                 "Failed to set initial state: %d\n", ret);
0142             goto err_enable;
0143         }
0144     }
0145 
0146     drvdata->enabled = pdata->init_on;
0147     platform_set_drvdata(pdev, drvdata);
0148 
0149     return 0;
0150 
0151 err_enable:
0152     sysfs_remove_group(&pdev->dev.kobj, &attr_group);
0153 
0154     return ret;
0155 }
0156 
0157 static int regulator_userspace_consumer_remove(struct platform_device *pdev)
0158 {
0159     struct userspace_consumer_data *data = platform_get_drvdata(pdev);
0160 
0161     sysfs_remove_group(&pdev->dev.kobj, &attr_group);
0162 
0163     if (data->enabled)
0164         regulator_bulk_disable(data->num_supplies, data->supplies);
0165 
0166     return 0;
0167 }
0168 
0169 static struct platform_driver regulator_userspace_consumer_driver = {
0170     .probe      = regulator_userspace_consumer_probe,
0171     .remove     = regulator_userspace_consumer_remove,
0172     .driver     = {
0173         .name       = "reg-userspace-consumer",
0174     },
0175 };
0176 
0177 module_platform_driver(regulator_userspace_consumer_driver);
0178 
0179 MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
0180 MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
0181 MODULE_LICENSE("GPL");