userspace-consumer.c 4.9 KB

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