18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * userspace-consumer.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2009 CompuLab, Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Mike Rapoport <mike@compulab.co.il> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based of virtual consumer driver: 108c2ecf20Sopenharmony_ci * Copyright 2008 Wolfson Microelectronics PLC. 118c2ecf20Sopenharmony_ci * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/regulator/userspace-consumer.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct userspace_consumer_data { 238c2ecf20Sopenharmony_ci const char *name; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci struct mutex lock; 268c2ecf20Sopenharmony_ci bool enabled; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci int num_supplies; 298c2ecf20Sopenharmony_ci struct regulator_bulk_data *supplies; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic ssize_t reg_show_name(struct device *dev, 338c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct userspace_consumer_data *data = dev_get_drvdata(dev); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", data->name); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic ssize_t reg_show_state(struct device *dev, 418c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct userspace_consumer_data *data = dev_get_drvdata(dev); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (data->enabled) 468c2ecf20Sopenharmony_ci return sprintf(buf, "enabled\n"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return sprintf(buf, "disabled\n"); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic ssize_t reg_set_state(struct device *dev, struct device_attribute *attr, 528c2ecf20Sopenharmony_ci const char *buf, size_t count) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct userspace_consumer_data *data = dev_get_drvdata(dev); 558c2ecf20Sopenharmony_ci bool enabled; 568c2ecf20Sopenharmony_ci int ret; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * sysfs_streq() doesn't need the \n's, but we add them so the strings 608c2ecf20Sopenharmony_ci * will be shared with show_state(), above. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1")) 638c2ecf20Sopenharmony_ci enabled = true; 648c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0")) 658c2ecf20Sopenharmony_ci enabled = false; 668c2ecf20Sopenharmony_ci else { 678c2ecf20Sopenharmony_ci dev_err(dev, "Configuring invalid mode\n"); 688c2ecf20Sopenharmony_ci return count; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 728c2ecf20Sopenharmony_ci if (enabled != data->enabled) { 738c2ecf20Sopenharmony_ci if (enabled) 748c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(data->num_supplies, 758c2ecf20Sopenharmony_ci data->supplies); 768c2ecf20Sopenharmony_ci else 778c2ecf20Sopenharmony_ci ret = regulator_bulk_disable(data->num_supplies, 788c2ecf20Sopenharmony_ci data->supplies); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (ret == 0) 818c2ecf20Sopenharmony_ci data->enabled = enabled; 828c2ecf20Sopenharmony_ci else 838c2ecf20Sopenharmony_ci dev_err(dev, "Failed to configure state: %d\n", ret); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return count; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0444, reg_show_name, NULL); 918c2ecf20Sopenharmony_cistatic DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic struct attribute *attributes[] = { 948c2ecf20Sopenharmony_ci &dev_attr_name.attr, 958c2ecf20Sopenharmony_ci &dev_attr_state.attr, 968c2ecf20Sopenharmony_ci NULL, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct attribute_group attr_group = { 1008c2ecf20Sopenharmony_ci .attrs = attributes, 1018c2ecf20Sopenharmony_ci}; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int regulator_userspace_consumer_probe(struct platform_device *pdev) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct regulator_userspace_consumer_data *pdata; 1068c2ecf20Sopenharmony_ci struct userspace_consumer_data *drvdata; 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 1108c2ecf20Sopenharmony_ci if (!pdata) 1118c2ecf20Sopenharmony_ci return -EINVAL; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci drvdata = devm_kzalloc(&pdev->dev, 1148c2ecf20Sopenharmony_ci sizeof(struct userspace_consumer_data), 1158c2ecf20Sopenharmony_ci GFP_KERNEL); 1168c2ecf20Sopenharmony_ci if (drvdata == NULL) 1178c2ecf20Sopenharmony_ci return -ENOMEM; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci drvdata->name = pdata->name; 1208c2ecf20Sopenharmony_ci drvdata->num_supplies = pdata->num_supplies; 1218c2ecf20Sopenharmony_ci drvdata->supplies = pdata->supplies; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci mutex_init(&drvdata->lock); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies, 1268c2ecf20Sopenharmony_ci drvdata->supplies); 1278c2ecf20Sopenharmony_ci if (ret) { 1288c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); 1338c2ecf20Sopenharmony_ci if (ret != 0) 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (pdata->init_on) { 1378c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(drvdata->num_supplies, 1388c2ecf20Sopenharmony_ci drvdata->supplies); 1398c2ecf20Sopenharmony_ci if (ret) { 1408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 1418c2ecf20Sopenharmony_ci "Failed to set initial state: %d\n", ret); 1428c2ecf20Sopenharmony_ci goto err_enable; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci drvdata->enabled = pdata->init_on; 1478c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, drvdata); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return 0; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cierr_enable: 1528c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &attr_group); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int regulator_userspace_consumer_remove(struct platform_device *pdev) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct userspace_consumer_data *data = platform_get_drvdata(pdev); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci sysfs_remove_group(&pdev->dev.kobj, &attr_group); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (data->enabled) 1648c2ecf20Sopenharmony_ci regulator_bulk_disable(data->num_supplies, data->supplies); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct platform_driver regulator_userspace_consumer_driver = { 1708c2ecf20Sopenharmony_ci .probe = regulator_userspace_consumer_probe, 1718c2ecf20Sopenharmony_ci .remove = regulator_userspace_consumer_remove, 1728c2ecf20Sopenharmony_ci .driver = { 1738c2ecf20Sopenharmony_ci .name = "reg-userspace-consumer", 1748c2ecf20Sopenharmony_ci }, 1758c2ecf20Sopenharmony_ci}; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cimodule_platform_driver(regulator_userspace_consumer_driver); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); 1808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); 1818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 182