userspace-consumer.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. #include <linux/err.h>
  14. #include <linux/mutex.h>
  15. #include <linux/module.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/regulator/consumer.h>
  18. #include <linux/regulator/userspace-consumer.h>
  19. #include <linux/slab.h>
  20. struct userspace_consumer_data {
  21. const char *name;
  22. struct mutex lock;
  23. bool enabled;
  24. int num_supplies;
  25. struct regulator_bulk_data *supplies;
  26. };
  27. static ssize_t reg_show_name(struct device *dev,
  28. struct device_attribute *attr, char *buf)
  29. {
  30. struct userspace_consumer_data *data = dev_get_drvdata(dev);
  31. return sprintf(buf, "%s\n", data->name);
  32. }
  33. static ssize_t reg_show_state(struct device *dev,
  34. struct device_attribute *attr, char *buf)
  35. {
  36. struct userspace_consumer_data *data = dev_get_drvdata(dev);
  37. if (data->enabled)
  38. return sprintf(buf, "enabled\n");
  39. return sprintf(buf, "disabled\n");
  40. }
  41. static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr,
  42. const char *buf, size_t count)
  43. {
  44. struct userspace_consumer_data *data = dev_get_drvdata(dev);
  45. bool enabled;
  46. int ret;
  47. /*
  48. * sysfs_streq() doesn't need the \n's, but we add them so the strings
  49. * will be shared with show_state(), above.
  50. */
  51. if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1"))
  52. enabled = true;
  53. else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0"))
  54. enabled = false;
  55. else {
  56. dev_err(dev, "Configuring invalid mode\n");
  57. return count;
  58. }
  59. mutex_lock(&data->lock);
  60. if (enabled != data->enabled) {
  61. if (enabled)
  62. ret = regulator_bulk_enable(data->num_supplies,
  63. data->supplies);
  64. else
  65. ret = regulator_bulk_disable(data->num_supplies,
  66. data->supplies);
  67. if (ret == 0)
  68. data->enabled = enabled;
  69. else
  70. dev_err(dev, "Failed to configure state: %d\n", ret);
  71. }
  72. mutex_unlock(&data->lock);
  73. return count;
  74. }
  75. static DEVICE_ATTR(name, 0444, reg_show_name, NULL);
  76. static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state);
  77. static struct attribute *attributes[] = {
  78. &dev_attr_name.attr,
  79. &dev_attr_state.attr,
  80. NULL,
  81. };
  82. static const struct attribute_group attr_group = {
  83. .attrs = attributes,
  84. };
  85. static int regulator_userspace_consumer_probe(struct platform_device *pdev)
  86. {
  87. struct regulator_userspace_consumer_data *pdata;
  88. struct userspace_consumer_data *drvdata;
  89. int ret;
  90. pdata = dev_get_platdata(&pdev->dev);
  91. if (!pdata)
  92. return -EINVAL;
  93. drvdata = devm_kzalloc(&pdev->dev,
  94. sizeof(struct userspace_consumer_data),
  95. GFP_KERNEL);
  96. if (drvdata == NULL)
  97. return -ENOMEM;
  98. drvdata->name = pdata->name;
  99. drvdata->num_supplies = pdata->num_supplies;
  100. drvdata->supplies = pdata->supplies;
  101. mutex_init(&drvdata->lock);
  102. ret = devm_regulator_bulk_get(&pdev->dev, drvdata->num_supplies,
  103. drvdata->supplies);
  104. if (ret) {
  105. dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret);
  106. return ret;
  107. }
  108. ret = sysfs_create_group(&pdev->dev.kobj, &attr_group);
  109. if (ret != 0)
  110. return ret;
  111. if (pdata->init_on) {
  112. ret = regulator_bulk_enable(drvdata->num_supplies,
  113. drvdata->supplies);
  114. if (ret) {
  115. dev_err(&pdev->dev,
  116. "Failed to set initial state: %d\n", ret);
  117. goto err_enable;
  118. }
  119. }
  120. drvdata->enabled = pdata->init_on;
  121. platform_set_drvdata(pdev, drvdata);
  122. return 0;
  123. err_enable:
  124. sysfs_remove_group(&pdev->dev.kobj, &attr_group);
  125. return ret;
  126. }
  127. static int regulator_userspace_consumer_remove(struct platform_device *pdev)
  128. {
  129. struct userspace_consumer_data *data = platform_get_drvdata(pdev);
  130. sysfs_remove_group(&pdev->dev.kobj, &attr_group);
  131. if (data->enabled)
  132. regulator_bulk_disable(data->num_supplies, data->supplies);
  133. return 0;
  134. }
  135. static struct platform_driver regulator_userspace_consumer_driver = {
  136. .probe = regulator_userspace_consumer_probe,
  137. .remove = regulator_userspace_consumer_remove,
  138. .driver = {
  139. .name = "reg-userspace-consumer",
  140. },
  141. };
  142. module_platform_driver(regulator_userspace_consumer_driver);
  143. MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
  144. MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
  145. MODULE_LICENSE("GPL");