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 22 struct 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 32 static 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 40 static 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 51 static 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 90 static DEVICE_ATTR_RO(name); 91 static DEVICE_ATTR_RW(state); 92 93 static struct attribute *attributes[] = { 94 &dev_attr_name.attr, 95 &dev_attr_state.attr, 96 NULL, 97 }; 98 99 static const struct attribute_group attr_group = { 100 .attrs = attributes, 101 }; 102 103 static 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 151 err_enable: 152 sysfs_remove_group(&pdev->dev.kobj, &attr_group); 153 154 return ret; 155 } 156 157 static 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 169 static 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 177 module_platform_driver(regulator_userspace_consumer_driver); 178 179 MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); 180 MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); 181 MODULE_LICENSE("GPL"); 182