11d98cccfSMike Rapoport /* 21d98cccfSMike Rapoport * userspace-consumer.c 31d98cccfSMike Rapoport * 41d98cccfSMike Rapoport * Copyright 2009 CompuLab, Ltd. 51d98cccfSMike Rapoport * 61d98cccfSMike Rapoport * Author: Mike Rapoport <mike@compulab.co.il> 71d98cccfSMike Rapoport * 81d98cccfSMike Rapoport * Based of virtual consumer driver: 91d98cccfSMike Rapoport * Copyright 2008 Wolfson Microelectronics PLC. 101d98cccfSMike Rapoport * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 111d98cccfSMike Rapoport * 121d98cccfSMike Rapoport * This program is free software; you can redistribute it and/or 131d98cccfSMike Rapoport * modify it under the terms of the GNU General Public License as 141d98cccfSMike Rapoport * published by the Free Software Foundation; either version 2 of the 151d98cccfSMike Rapoport * License, or (at your option) any later version. 161d98cccfSMike Rapoport * 171d98cccfSMike Rapoport */ 181d98cccfSMike Rapoport 191d98cccfSMike Rapoport #include <linux/err.h> 201d98cccfSMike Rapoport #include <linux/mutex.h> 2165602c32SPaul Gortmaker #include <linux/module.h> 221d98cccfSMike Rapoport #include <linux/platform_device.h> 231d98cccfSMike Rapoport #include <linux/regulator/consumer.h> 241d98cccfSMike Rapoport #include <linux/regulator/userspace-consumer.h> 255a0e3ad6STejun Heo #include <linux/slab.h> 261d98cccfSMike Rapoport 271d98cccfSMike Rapoport struct userspace_consumer_data { 281d98cccfSMike Rapoport const char *name; 291d98cccfSMike Rapoport 301d98cccfSMike Rapoport struct mutex lock; 311d98cccfSMike Rapoport bool enabled; 321d98cccfSMike Rapoport 331d98cccfSMike Rapoport int num_supplies; 341d98cccfSMike Rapoport struct regulator_bulk_data *supplies; 351d98cccfSMike Rapoport }; 361d98cccfSMike Rapoport 377c314991SLiam Girdwood static ssize_t reg_show_name(struct device *dev, 381d98cccfSMike Rapoport struct device_attribute *attr, char *buf) 391d98cccfSMike Rapoport { 401d98cccfSMike Rapoport struct userspace_consumer_data *data = dev_get_drvdata(dev); 411d98cccfSMike Rapoport 421d98cccfSMike Rapoport return sprintf(buf, "%s\n", data->name); 431d98cccfSMike Rapoport } 441d98cccfSMike Rapoport 457c314991SLiam Girdwood static ssize_t reg_show_state(struct device *dev, 461d98cccfSMike Rapoport struct device_attribute *attr, char *buf) 471d98cccfSMike Rapoport { 481d98cccfSMike Rapoport struct userspace_consumer_data *data = dev_get_drvdata(dev); 491d98cccfSMike Rapoport 501d98cccfSMike Rapoport if (data->enabled) 511d98cccfSMike Rapoport return sprintf(buf, "enabled\n"); 521d98cccfSMike Rapoport 531d98cccfSMike Rapoport return sprintf(buf, "disabled\n"); 541d98cccfSMike Rapoport } 551d98cccfSMike Rapoport 567c314991SLiam Girdwood static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr, 571d98cccfSMike Rapoport const char *buf, size_t count) 581d98cccfSMike Rapoport { 591d98cccfSMike Rapoport struct userspace_consumer_data *data = dev_get_drvdata(dev); 601d98cccfSMike Rapoport bool enabled; 611d98cccfSMike Rapoport int ret; 621d98cccfSMike Rapoport 631d98cccfSMike Rapoport /* 641d98cccfSMike Rapoport * sysfs_streq() doesn't need the \n's, but we add them so the strings 651d98cccfSMike Rapoport * will be shared with show_state(), above. 661d98cccfSMike Rapoport */ 671d98cccfSMike Rapoport if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1")) 681d98cccfSMike Rapoport enabled = true; 691d98cccfSMike Rapoport else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0")) 701d98cccfSMike Rapoport enabled = false; 711d98cccfSMike Rapoport else { 721d98cccfSMike Rapoport dev_err(dev, "Configuring invalid mode\n"); 731d98cccfSMike Rapoport return count; 741d98cccfSMike Rapoport } 751d98cccfSMike Rapoport 761d98cccfSMike Rapoport mutex_lock(&data->lock); 771d98cccfSMike Rapoport if (enabled != data->enabled) { 781d98cccfSMike Rapoport if (enabled) 791d98cccfSMike Rapoport ret = regulator_bulk_enable(data->num_supplies, 801d98cccfSMike Rapoport data->supplies); 811d98cccfSMike Rapoport else 821d98cccfSMike Rapoport ret = regulator_bulk_disable(data->num_supplies, 831d98cccfSMike Rapoport data->supplies); 841d98cccfSMike Rapoport 851d98cccfSMike Rapoport if (ret == 0) 861d98cccfSMike Rapoport data->enabled = enabled; 871d98cccfSMike Rapoport else 881d98cccfSMike Rapoport dev_err(dev, "Failed to configure state: %d\n", ret); 891d98cccfSMike Rapoport } 901d98cccfSMike Rapoport mutex_unlock(&data->lock); 911d98cccfSMike Rapoport 921d98cccfSMike Rapoport return count; 931d98cccfSMike Rapoport } 941d98cccfSMike Rapoport 957c314991SLiam Girdwood static DEVICE_ATTR(name, 0444, reg_show_name, NULL); 967c314991SLiam Girdwood static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state); 971d98cccfSMike Rapoport 98e9d62698SFelipe Balbi static struct attribute *attributes[] = { 99e9d62698SFelipe Balbi &dev_attr_name.attr, 100e9d62698SFelipe Balbi &dev_attr_state.attr, 101e9d62698SFelipe Balbi NULL, 102e9d62698SFelipe Balbi }; 103e9d62698SFelipe Balbi 104e9d62698SFelipe Balbi static const struct attribute_group attr_group = { 105e9d62698SFelipe Balbi .attrs = attributes, 1061d98cccfSMike Rapoport }; 1071d98cccfSMike Rapoport 1081d98cccfSMike Rapoport static int regulator_userspace_consumer_probe(struct platform_device *pdev) 1091d98cccfSMike Rapoport { 1101d98cccfSMike Rapoport struct regulator_userspace_consumer_data *pdata; 1111d98cccfSMike Rapoport struct userspace_consumer_data *drvdata; 112e9d62698SFelipe Balbi int ret; 1131d98cccfSMike Rapoport 1141d98cccfSMike Rapoport pdata = pdev->dev.platform_data; 1151d98cccfSMike Rapoport if (!pdata) 1161d98cccfSMike Rapoport return -EINVAL; 1171d98cccfSMike Rapoport 1181d98cccfSMike Rapoport drvdata = kzalloc(sizeof(struct userspace_consumer_data), GFP_KERNEL); 1191d98cccfSMike Rapoport if (drvdata == NULL) 1201d98cccfSMike Rapoport return -ENOMEM; 1211d98cccfSMike Rapoport 1221d98cccfSMike Rapoport drvdata->name = pdata->name; 1231d98cccfSMike Rapoport drvdata->num_supplies = pdata->num_supplies; 1241d98cccfSMike Rapoport drvdata->supplies = pdata->supplies; 1251d98cccfSMike Rapoport 1261d98cccfSMike Rapoport mutex_init(&drvdata->lock); 1271d98cccfSMike Rapoport 1281d98cccfSMike Rapoport ret = regulator_bulk_get(&pdev->dev, drvdata->num_supplies, 1291d98cccfSMike Rapoport drvdata->supplies); 1301d98cccfSMike Rapoport if (ret) { 1311d98cccfSMike Rapoport dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); 1321d98cccfSMike Rapoport goto err_alloc_supplies; 1331d98cccfSMike Rapoport } 1341d98cccfSMike Rapoport 135e9d62698SFelipe Balbi ret = sysfs_create_group(&pdev->dev.kobj, &attr_group); 1361d98cccfSMike Rapoport if (ret != 0) 1371d98cccfSMike Rapoport goto err_create_attrs; 1381d98cccfSMike Rapoport 139e9d62698SFelipe Balbi if (pdata->init_on) { 1401d98cccfSMike Rapoport ret = regulator_bulk_enable(drvdata->num_supplies, 1411d98cccfSMike Rapoport drvdata->supplies); 1421d98cccfSMike Rapoport if (ret) { 143e9d62698SFelipe Balbi dev_err(&pdev->dev, 144e9d62698SFelipe Balbi "Failed to set initial state: %d\n", ret); 145e9d62698SFelipe Balbi goto err_enable; 146e9d62698SFelipe Balbi } 1471d98cccfSMike Rapoport } 1481d98cccfSMike Rapoport 149e9d62698SFelipe Balbi drvdata->enabled = pdata->init_on; 1501d98cccfSMike Rapoport platform_set_drvdata(pdev, drvdata); 1511d98cccfSMike Rapoport 1521d98cccfSMike Rapoport return 0; 1531d98cccfSMike Rapoport 154e9d62698SFelipe Balbi err_enable: 155e9d62698SFelipe Balbi sysfs_remove_group(&pdev->dev.kobj, &attr_group); 1561d98cccfSMike Rapoport 157e9d62698SFelipe Balbi err_create_attrs: 1581d98cccfSMike Rapoport regulator_bulk_free(drvdata->num_supplies, drvdata->supplies); 1591d98cccfSMike Rapoport 1601d98cccfSMike Rapoport err_alloc_supplies: 1611d98cccfSMike Rapoport kfree(drvdata); 1621d98cccfSMike Rapoport return ret; 1631d98cccfSMike Rapoport } 1641d98cccfSMike Rapoport 1651d98cccfSMike Rapoport static int regulator_userspace_consumer_remove(struct platform_device *pdev) 1661d98cccfSMike Rapoport { 1671d98cccfSMike Rapoport struct userspace_consumer_data *data = platform_get_drvdata(pdev); 1681d98cccfSMike Rapoport 169e9d62698SFelipe Balbi sysfs_remove_group(&pdev->dev.kobj, &attr_group); 1701d98cccfSMike Rapoport 1711d98cccfSMike Rapoport if (data->enabled) 1721d98cccfSMike Rapoport regulator_bulk_disable(data->num_supplies, data->supplies); 1731d98cccfSMike Rapoport 1741d98cccfSMike Rapoport regulator_bulk_free(data->num_supplies, data->supplies); 1751d98cccfSMike Rapoport kfree(data); 1761d98cccfSMike Rapoport 1771d98cccfSMike Rapoport return 0; 1781d98cccfSMike Rapoport } 1791d98cccfSMike Rapoport 1801d98cccfSMike Rapoport static struct platform_driver regulator_userspace_consumer_driver = { 1811d98cccfSMike Rapoport .probe = regulator_userspace_consumer_probe, 1821d98cccfSMike Rapoport .remove = regulator_userspace_consumer_remove, 1831d98cccfSMike Rapoport .driver = { 1841d98cccfSMike Rapoport .name = "reg-userspace-consumer", 1851d98cccfSMike Rapoport }, 1861d98cccfSMike Rapoport }; 1871d98cccfSMike Rapoport 188005d610fSAxel Lin module_platform_driver(regulator_userspace_consumer_driver); 1891d98cccfSMike Rapoport 1901d98cccfSMike Rapoport MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); 1911d98cccfSMike Rapoport MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); 1921d98cccfSMike Rapoport MODULE_LICENSE("GPL"); 193