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/of.h> 18 #include <linux/platform_device.h> 19 #include <linux/regulator/consumer.h> 20 #include <linux/regulator/userspace-consumer.h> 21 #include <linux/slab.h> 22 23 struct userspace_consumer_data { 24 const char *name; 25 26 struct mutex lock; 27 bool enabled; 28 bool no_autoswitch; 29 30 int num_supplies; 31 struct regulator_bulk_data *supplies; 32 }; 33 34 static ssize_t name_show(struct device *dev, 35 struct device_attribute *attr, char *buf) 36 { 37 struct userspace_consumer_data *data = dev_get_drvdata(dev); 38 39 return sprintf(buf, "%s\n", data->name); 40 } 41 42 static ssize_t state_show(struct device *dev, 43 struct device_attribute *attr, char *buf) 44 { 45 struct userspace_consumer_data *data = dev_get_drvdata(dev); 46 47 if (data->enabled) 48 return sprintf(buf, "enabled\n"); 49 50 return sprintf(buf, "disabled\n"); 51 } 52 53 static ssize_t state_store(struct device *dev, struct device_attribute *attr, 54 const char *buf, size_t count) 55 { 56 struct userspace_consumer_data *data = dev_get_drvdata(dev); 57 bool enabled; 58 int ret; 59 60 /* 61 * sysfs_streq() doesn't need the \n's, but we add them so the strings 62 * will be shared with show_state(), above. 63 */ 64 if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1")) 65 enabled = true; 66 else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0")) 67 enabled = false; 68 else { 69 dev_err(dev, "Configuring invalid mode\n"); 70 return count; 71 } 72 73 mutex_lock(&data->lock); 74 if (enabled != data->enabled) { 75 if (enabled) 76 ret = regulator_bulk_enable(data->num_supplies, 77 data->supplies); 78 else 79 ret = regulator_bulk_disable(data->num_supplies, 80 data->supplies); 81 82 if (ret == 0) 83 data->enabled = enabled; 84 else 85 dev_err(dev, "Failed to configure state: %d\n", ret); 86 } 87 mutex_unlock(&data->lock); 88 89 return count; 90 } 91 92 static DEVICE_ATTR_RO(name); 93 static DEVICE_ATTR_RW(state); 94 95 static struct attribute *attributes[] = { 96 &dev_attr_name.attr, 97 &dev_attr_state.attr, 98 NULL, 99 }; 100 101 static umode_t attr_visible(struct kobject *kobj, struct attribute *attr, int idx) 102 { 103 struct device *dev = kobj_to_dev(kobj); 104 struct userspace_consumer_data *data = dev_get_drvdata(dev); 105 106 /* If a name hasn't been set, don't bother with the attribute */ 107 if (attr == &dev_attr_name.attr && !data->name) 108 return 0; 109 110 return attr->mode; 111 } 112 113 static const struct attribute_group attr_group = { 114 .attrs = attributes, 115 .is_visible = attr_visible, 116 }; 117 118 static int regulator_userspace_consumer_probe(struct platform_device *pdev) 119 { 120 struct regulator_userspace_consumer_data tmpdata; 121 struct regulator_userspace_consumer_data *pdata; 122 struct userspace_consumer_data *drvdata; 123 int ret; 124 125 pdata = dev_get_platdata(&pdev->dev); 126 if (!pdata) { 127 if (!pdev->dev.of_node) 128 return -EINVAL; 129 130 pdata = &tmpdata; 131 memset(pdata, 0, sizeof(*pdata)); 132 133 pdata->no_autoswitch = true; 134 pdata->num_supplies = 1; 135 pdata->supplies = devm_kzalloc(&pdev->dev, sizeof(*pdata->supplies), GFP_KERNEL); 136 if (!pdata->supplies) 137 return -ENOMEM; 138 pdata->supplies[0].supply = "vout"; 139 } 140 141 if (pdata->num_supplies < 1) { 142 dev_err(&pdev->dev, "At least one supply required\n"); 143 return -EINVAL; 144 } 145 146 drvdata = devm_kzalloc(&pdev->dev, 147 sizeof(struct userspace_consumer_data), 148 GFP_KERNEL); 149 if (drvdata == NULL) 150 return -ENOMEM; 151 152 drvdata->name = pdata->name; 153 drvdata->num_supplies = pdata->num_supplies; 154 drvdata->supplies = pdata->supplies; 155 drvdata->no_autoswitch = pdata->no_autoswitch; 156 157 mutex_init(&drvdata->lock); 158 159 ret = devm_regulator_bulk_get_exclusive(&pdev->dev, drvdata->num_supplies, 160 drvdata->supplies); 161 if (ret) { 162 dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); 163 return ret; 164 } 165 166 platform_set_drvdata(pdev, drvdata); 167 168 ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); 169 if (ret != 0) 170 return ret; 171 172 if (pdata->init_on && !pdata->no_autoswitch) { 173 ret = regulator_bulk_enable(drvdata->num_supplies, 174 drvdata->supplies); 175 if (ret) { 176 dev_err(&pdev->dev, 177 "Failed to set initial state: %d\n", ret); 178 goto err_enable; 179 } 180 } 181 182 ret = regulator_is_enabled(pdata->supplies[0].consumer); 183 if (ret < 0) { 184 dev_err(&pdev->dev, "Failed to get regulator status\n"); 185 goto err_enable; 186 } 187 drvdata->enabled = !!ret; 188 189 return 0; 190 191 err_enable: 192 sysfs_remove_group(&pdev->dev.kobj, &attr_group); 193 194 return ret; 195 } 196 197 static int regulator_userspace_consumer_remove(struct platform_device *pdev) 198 { 199 struct userspace_consumer_data *data = platform_get_drvdata(pdev); 200 201 sysfs_remove_group(&pdev->dev.kobj, &attr_group); 202 203 if (data->enabled && !data->no_autoswitch) 204 regulator_bulk_disable(data->num_supplies, data->supplies); 205 206 return 0; 207 } 208 209 static const struct of_device_id regulator_userspace_consumer_of_match[] = { 210 { .compatible = "regulator-output", }, 211 {}, 212 }; 213 MODULE_DEVICE_TABLE(of, regulator_userspace_consumer_of_match); 214 215 static struct platform_driver regulator_userspace_consumer_driver = { 216 .probe = regulator_userspace_consumer_probe, 217 .remove = regulator_userspace_consumer_remove, 218 .driver = { 219 .name = "reg-userspace-consumer", 220 .probe_type = PROBE_PREFER_ASYNCHRONOUS, 221 .of_match_table = regulator_userspace_consumer_of_match, 222 }, 223 }; 224 225 module_platform_driver(regulator_userspace_consumer_driver); 226 227 MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); 228 MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); 229 MODULE_LICENSE("GPL"); 230