userspace-consumer.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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 device_attribute *attributes[] = {
  81. &dev_attr_name,
  82. &dev_attr_state,
  83. };
  84. static int regulator_userspace_consumer_probe(struct platform_device *pdev)
  85. {
  86. struct regulator_userspace_consumer_data *pdata;
  87. struct userspace_consumer_data *drvdata;
  88. int ret, i;
  89. pdata = pdev->dev.platform_data;
  90. if (!pdata)
  91. return -EINVAL;
  92. drvdata = kzalloc(sizeof(struct userspace_consumer_data), GFP_KERNEL);
  93. if (drvdata == NULL)
  94. return -ENOMEM;
  95. drvdata->name = pdata->name;
  96. drvdata->num_supplies = pdata->num_supplies;
  97. drvdata->supplies = pdata->supplies;
  98. mutex_init(&drvdata->lock);
  99. ret = regulator_bulk_get(&pdev->dev, drvdata->num_supplies,
  100. drvdata->supplies);
  101. if (ret) {
  102. dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret);
  103. goto err_alloc_supplies;
  104. }
  105. for (i = 0; i < ARRAY_SIZE(attributes); i++) {
  106. ret = device_create_file(&pdev->dev, attributes[i]);
  107. if (ret != 0)
  108. goto err_create_attrs;
  109. }
  110. if (pdata->init_on)
  111. ret = regulator_bulk_enable(drvdata->num_supplies,
  112. drvdata->supplies);
  113. drvdata->enabled = pdata->init_on;
  114. if (ret) {
  115. dev_err(&pdev->dev, "Failed to set initial state: %d\n", ret);
  116. goto err_create_attrs;
  117. }
  118. platform_set_drvdata(pdev, drvdata);
  119. return 0;
  120. err_create_attrs:
  121. for (i = 0; i < ARRAY_SIZE(attributes); i++)
  122. device_remove_file(&pdev->dev, attributes[i]);
  123. regulator_bulk_free(drvdata->num_supplies, drvdata->supplies);
  124. err_alloc_supplies:
  125. kfree(drvdata);
  126. return ret;
  127. }
  128. static int regulator_userspace_consumer_remove(struct platform_device *pdev)
  129. {
  130. struct userspace_consumer_data *data = platform_get_drvdata(pdev);
  131. int i;
  132. for (i = 0; i < ARRAY_SIZE(attributes); i++)
  133. device_remove_file(&pdev->dev, attributes[i]);
  134. if (data->enabled)
  135. regulator_bulk_disable(data->num_supplies, data->supplies);
  136. regulator_bulk_free(data->num_supplies, data->supplies);
  137. kfree(data);
  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. static int __init regulator_userspace_consumer_init(void)
  148. {
  149. return platform_driver_register(&regulator_userspace_consumer_driver);
  150. }
  151. module_init(regulator_userspace_consumer_init);
  152. static void __exit regulator_userspace_consumer_exit(void)
  153. {
  154. platform_driver_unregister(&regulator_userspace_consumer_driver);
  155. }
  156. module_exit(regulator_userspace_consumer_exit);
  157. MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
  158. MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators");
  159. MODULE_LICENSE("GPL");