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 
1185abe0c40SAxel Lin 	drvdata = devm_kzalloc(&pdev->dev,
1195abe0c40SAxel Lin 			       sizeof(struct userspace_consumer_data),
1205abe0c40SAxel Lin 			       GFP_KERNEL);
1211d98cccfSMike Rapoport 	if (drvdata == NULL)
1221d98cccfSMike Rapoport 		return -ENOMEM;
1231d98cccfSMike Rapoport 
1241d98cccfSMike Rapoport 	drvdata->name = pdata->name;
1251d98cccfSMike Rapoport 	drvdata->num_supplies = pdata->num_supplies;
1261d98cccfSMike Rapoport 	drvdata->supplies = pdata->supplies;
1271d98cccfSMike Rapoport 
1281d98cccfSMike Rapoport 	mutex_init(&drvdata->lock);
1291d98cccfSMike Rapoport 
1305abe0c40SAxel Lin 	ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies,
1311d98cccfSMike Rapoport 				      drvdata->supplies);
1321d98cccfSMike Rapoport 	if (ret) {
1331d98cccfSMike Rapoport 		dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret);
1345abe0c40SAxel Lin 		return ret;
1351d98cccfSMike Rapoport 	}
1361d98cccfSMike Rapoport 
137e9d62698SFelipe Balbi 	ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
1381d98cccfSMike Rapoport 	if (ret != 0)
1395abe0c40SAxel Lin 		return ret;
1401d98cccfSMike Rapoport 
141e9d62698SFelipe Balbi 	if (pdata->init_on) {
1421d98cccfSMike Rapoport 		ret = regulator_bulk_enable(drvdata->num_supplies,
1431d98cccfSMike Rapoport 					    drvdata->supplies);
1441d98cccfSMike Rapoport 		if (ret) {
145e9d62698SFelipe Balbi 			dev_err(&pdev->dev,
146e9d62698SFelipe Balbi 				"Failed to set initial state: %d\n", ret);
147e9d62698SFelipe Balbi 			goto err_enable;
148e9d62698SFelipe Balbi 		}
1491d98cccfSMike Rapoport 	}
1501d98cccfSMike Rapoport 
151e9d62698SFelipe Balbi 	drvdata->enabled = pdata->init_on;
1521d98cccfSMike Rapoport 	platform_set_drvdata(pdev, drvdata);
1531d98cccfSMike Rapoport 
1541d98cccfSMike Rapoport 	return 0;
1551d98cccfSMike Rapoport 
156e9d62698SFelipe Balbi err_enable:
157e9d62698SFelipe Balbi 	sysfs_remove_group(&pdev->dev.kobj, &attr_group);
1581d98cccfSMike Rapoport 
1591d98cccfSMike Rapoport 	return ret;
1601d98cccfSMike Rapoport }
1611d98cccfSMike Rapoport 
1621d98cccfSMike Rapoport static int regulator_userspace_consumer_remove(struct platform_device *pdev)
1631d98cccfSMike Rapoport {
1641d98cccfSMike Rapoport 	struct userspace_consumer_data *data = platform_get_drvdata(pdev);
1651d98cccfSMike Rapoport 
166e9d62698SFelipe Balbi 	sysfs_remove_group(&pdev->dev.kobj, &attr_group);
1671d98cccfSMike Rapoport 
1681d98cccfSMike Rapoport 	if (data->enabled)
1691d98cccfSMike Rapoport 		regulator_bulk_disable(data->num_supplies, data->supplies);
1701d98cccfSMike Rapoport 
1711d98cccfSMike Rapoport 	return 0;
1721d98cccfSMike Rapoport }
1731d98cccfSMike Rapoport 
1741d98cccfSMike Rapoport static struct platform_driver regulator_userspace_consumer_driver = {
1751d98cccfSMike Rapoport 	.probe		= regulator_userspace_consumer_probe,
1761d98cccfSMike Rapoport 	.remove		= regulator_userspace_consumer_remove,
1771d98cccfSMike Rapoport 	.driver		= {
1781d98cccfSMike Rapoport 		.name		= "reg-userspace-consumer",
1791d98cccfSMike Rapoport 	},
1801d98cccfSMike Rapoport };
1811d98cccfSMike Rapoport 
182005d610fSAxel Lin module_platform_driver(regulator_userspace_consumer_driver);
1831d98cccfSMike Rapoport 
1841d98cccfSMike Rapoport MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
1851d98cccfSMike Rapoport MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
1861d98cccfSMike Rapoport MODULE_LICENSE("GPL");
187