pwm-fan.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * pwm-fan.c - Hwmon driver for fans connected to PWM lines.
  3. *
  4. * Copyright (c) 2014 Samsung Electronics Co., Ltd.
  5. *
  6. * Author: Kamil Debski <k.debski@samsung.com>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. */
  18. #include <linux/hwmon.h>
  19. #include <linux/hwmon-sysfs.h>
  20. #include <linux/module.h>
  21. #include <linux/mutex.h>
  22. #include <linux/of.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/pwm.h>
  25. #include <linux/sysfs.h>
  26. #define MAX_PWM 255
  27. struct pwm_fan_ctx {
  28. struct mutex lock;
  29. struct pwm_device *pwm;
  30. unsigned char pwm_value;
  31. };
  32. static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
  33. const char *buf, size_t count)
  34. {
  35. struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  36. unsigned long pwm, duty;
  37. ssize_t ret;
  38. if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
  39. return -EINVAL;
  40. mutex_lock(&ctx->lock);
  41. if (ctx->pwm_value == pwm)
  42. goto exit_set_pwm_no_change;
  43. if (pwm == 0) {
  44. pwm_disable(ctx->pwm);
  45. goto exit_set_pwm;
  46. }
  47. duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM);
  48. ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
  49. if (ret)
  50. goto exit_set_pwm_err;
  51. if (ctx->pwm_value == 0) {
  52. ret = pwm_enable(ctx->pwm);
  53. if (ret)
  54. goto exit_set_pwm_err;
  55. }
  56. exit_set_pwm:
  57. ctx->pwm_value = pwm;
  58. exit_set_pwm_no_change:
  59. ret = count;
  60. exit_set_pwm_err:
  61. mutex_unlock(&ctx->lock);
  62. return ret;
  63. }
  64. static ssize_t show_pwm(struct device *dev,
  65. struct device_attribute *attr, char *buf)
  66. {
  67. struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  68. return sprintf(buf, "%u\n", ctx->pwm_value);
  69. }
  70. static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0);
  71. static struct attribute *pwm_fan_attrs[] = {
  72. &sensor_dev_attr_pwm1.dev_attr.attr,
  73. NULL,
  74. };
  75. ATTRIBUTE_GROUPS(pwm_fan);
  76. static int pwm_fan_probe(struct platform_device *pdev)
  77. {
  78. struct device *hwmon;
  79. struct pwm_fan_ctx *ctx;
  80. int duty_cycle;
  81. int ret;
  82. ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
  83. if (!ctx)
  84. return -ENOMEM;
  85. mutex_init(&ctx->lock);
  86. ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL);
  87. if (IS_ERR(ctx->pwm)) {
  88. dev_err(&pdev->dev, "Could not get PWM\n");
  89. return PTR_ERR(ctx->pwm);
  90. }
  91. platform_set_drvdata(pdev, ctx);
  92. /* Set duty cycle to maximum allowed */
  93. duty_cycle = ctx->pwm->period - 1;
  94. ctx->pwm_value = MAX_PWM;
  95. ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period);
  96. if (ret) {
  97. dev_err(&pdev->dev, "Failed to configure PWM\n");
  98. return ret;
  99. }
  100. /* Enbale PWM output */
  101. ret = pwm_enable(ctx->pwm);
  102. if (ret) {
  103. dev_err(&pdev->dev, "Failed to enable PWM\n");
  104. return ret;
  105. }
  106. hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan",
  107. ctx, pwm_fan_groups);
  108. if (IS_ERR(hwmon)) {
  109. dev_err(&pdev->dev, "Failed to register hwmon device\n");
  110. pwm_disable(ctx->pwm);
  111. return PTR_ERR(hwmon);
  112. }
  113. return 0;
  114. }
  115. static int pwm_fan_remove(struct platform_device *pdev)
  116. {
  117. struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
  118. if (ctx->pwm_value)
  119. pwm_disable(ctx->pwm);
  120. return 0;
  121. }
  122. #ifdef CONFIG_PM_SLEEP
  123. static int pwm_fan_suspend(struct device *dev)
  124. {
  125. struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  126. if (ctx->pwm_value)
  127. pwm_disable(ctx->pwm);
  128. return 0;
  129. }
  130. static int pwm_fan_resume(struct device *dev)
  131. {
  132. struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
  133. if (ctx->pwm_value)
  134. return pwm_enable(ctx->pwm);
  135. return 0;
  136. }
  137. #endif
  138. static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume);
  139. static struct of_device_id of_pwm_fan_match[] = {
  140. { .compatible = "pwm-fan", },
  141. {},
  142. };
  143. static struct platform_driver pwm_fan_driver = {
  144. .probe = pwm_fan_probe,
  145. .remove = pwm_fan_remove,
  146. .driver = {
  147. .name = "pwm-fan",
  148. .pm = &pwm_fan_pm,
  149. .of_match_table = of_pwm_fan_match,
  150. },
  151. };
  152. module_platform_driver(pwm_fan_driver);
  153. MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
  154. MODULE_ALIAS("platform:pwm-fan");
  155. MODULE_DESCRIPTION("PWM FAN driver");
  156. MODULE_LICENSE("GPL");