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