userspace-consumer.c 4.5 KB

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