aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/hwmon/pwm-fan.txt12
-rw-r--r--Documentation/hwmon/pwm-fan17
-rw-r--r--drivers/hwmon/Kconfig11
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/pwm-fan.c194
5 files changed, 235 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.txt b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
new file mode 100644
index 000000000000..610757ce4492
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.txt
@@ -0,0 +1,12 @@
1Bindings for a fan connected to the PWM lines
2
3Required properties:
4- compatible : "pwm-fan"
5- pwms : the PWM that is used to control the PWM fan
6
7Example:
8 pwm-fan {
9 compatible = "pwm-fan";
10 status = "okay";
11 pwms = <&pwm 0 10000 0>;
12 };
diff --git a/Documentation/hwmon/pwm-fan b/Documentation/hwmon/pwm-fan
new file mode 100644
index 000000000000..18529d2e3bcf
--- /dev/null
+++ b/Documentation/hwmon/pwm-fan
@@ -0,0 +1,17 @@
1Kernel driver pwm-fan
2=====================
3
4This driver enables the use of a PWM module to drive a fan. It uses the
5generic PWM interface thus it is hardware independent. It can be used on
6many SoCs, as long as the SoC supplies a PWM line driver that exposes
7the generic PWM API.
8
9Author: Kamil Debski <k.debski@samsung.com>
10
11Description
12-----------
13
14The driver implements a simple interface for driving a fan connected to
15a PWM output. It uses the generic PWM interface, thus it can be used with
16a range of SoCs. The driver exposes the fan to the user space through
17the hwmon's sysfs interface.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index c0aec8db462f..b93ab97732c0 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1128,6 +1128,17 @@ config SENSORS_PCF8591
1128 1128
1129source drivers/hwmon/pmbus/Kconfig 1129source drivers/hwmon/pmbus/Kconfig
1130 1130
1131config SENSORS_PWM_FAN
1132 tristate "PWM fan"
1133 depends on PWM
1134 help
1135 If you say yes here you get support for fans connected to PWM lines.
1136 The driver uses the generic PWM interface, thus it will work on a
1137 variety of SoCs.
1138
1139 This driver can also be built as a module. If so, the module
1140 will be called pwm-fan.
1141
1131config SENSORS_SHT15 1142config SENSORS_SHT15
1132 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." 1143 tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
1133 depends on GPIOLIB 1144 depends on GPIOLIB
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index f4435ea6b169..be28152c9848 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
122obj-$(CONFIG_SENSORS_PC87427) += pc87427.o 122obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
123obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o 123obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
124obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o 124obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
125obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
125obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o 126obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o
126obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o 127obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
127obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o 128obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
new file mode 100644
index 000000000000..af30dd63de6a
--- /dev/null
+++ b/drivers/hwmon/pwm-fan.c
@@ -0,0 +1,194 @@
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
19#include <linux/hwmon.h>
20#include <linux/hwmon-sysfs.h>
21#include <linux/module.h>
22#include <linux/mutex.h>
23#include <linux/of.h>
24#include <linux/platform_device.h>
25#include <linux/pwm.h>
26#include <linux/sysfs.h>
27
28#define MAX_PWM 255
29
30struct pwm_fan_ctx {
31 struct mutex lock;
32 struct pwm_device *pwm;
33 unsigned char pwm_value;
34};
35
36static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
37 const char *buf, size_t count)
38{
39 struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
40 unsigned long pwm, duty;
41 ssize_t ret;
42
43 if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM)
44 return -EINVAL;
45
46 mutex_lock(&ctx->lock);
47
48 if (ctx->pwm_value == pwm)
49 goto exit_set_pwm_no_change;
50
51 if (pwm == 0) {
52 pwm_disable(ctx->pwm);
53 goto exit_set_pwm;
54 }
55
56 duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM);
57 ret = pwm_config(ctx->pwm, duty, ctx->pwm->period);
58 if (ret)
59 goto exit_set_pwm_err;
60
61 if (ctx->pwm_value == 0) {
62 ret = pwm_enable(ctx->pwm);
63 if (ret)
64 goto exit_set_pwm_err;
65 }
66
67exit_set_pwm:
68 ctx->pwm_value = pwm;
69exit_set_pwm_no_change:
70 ret = count;
71exit_set_pwm_err:
72 mutex_unlock(&ctx->lock);
73 return ret;
74}
75
76static ssize_t show_pwm(struct device *dev,
77 struct device_attribute *attr, char *buf)
78{
79 struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
80
81 return sprintf(buf, "%u\n", ctx->pwm_value);
82}
83
84
85static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0);
86
87static struct attribute *pwm_fan_attrs[] = {
88 &sensor_dev_attr_pwm1.dev_attr.attr,
89 NULL,
90};
91
92ATTRIBUTE_GROUPS(pwm_fan);
93
94static int pwm_fan_probe(struct platform_device *pdev)
95{
96 struct device *hwmon;
97 struct pwm_fan_ctx *ctx;
98 int duty_cycle;
99 int ret;
100
101 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
102 if (!ctx)
103 return -ENOMEM;
104
105 mutex_init(&ctx->lock);
106
107 ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL);
108 if (IS_ERR(ctx->pwm)) {
109 dev_err(&pdev->dev, "Could not get PWM\n");
110 return PTR_ERR(ctx->pwm);
111 }
112
113 dev_set_drvdata(&pdev->dev, ctx);
114 platform_set_drvdata(pdev, ctx);
115
116 /* Set duty cycle to maximum allowed */
117 duty_cycle = ctx->pwm->period - 1;
118 ctx->pwm_value = MAX_PWM;
119
120 ret = pwm_config(ctx->pwm, duty_cycle, ctx->pwm->period);
121 if (ret) {
122 dev_err(&pdev->dev, "Failed to configure PWM\n");
123 return ret;
124 }
125
126 /* Enbale PWM output */
127 ret = pwm_enable(ctx->pwm);
128 if (ret) {
129 dev_err(&pdev->dev, "Failed to enable PWM\n");
130 return ret;
131 }
132
133 hwmon = devm_hwmon_device_register_with_groups(&pdev->dev, "pwmfan",
134 ctx, pwm_fan_groups);
135 if (IS_ERR(hwmon)) {
136 dev_err(&pdev->dev, "Failed to register hwmon device\n");
137 pwm_disable(ctx->pwm);
138 return PTR_ERR(hwmon);
139 }
140 return 0;
141}
142
143static int pwm_fan_remove(struct platform_device *pdev)
144{
145 struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
146
147 if (ctx->pwm_value)
148 pwm_disable(ctx->pwm);
149 return 0;
150}
151
152#ifdef CONFIG_PM_SLEEP
153static int pwm_fan_suspend(struct device *dev)
154{
155 struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
156
157 if (ctx->pwm_value)
158 pwm_disable(ctx->pwm);
159 return 0;
160}
161
162static int pwm_fan_resume(struct device *dev)
163{
164 struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
165
166 if (ctx->pwm_value)
167 return pwm_enable(ctx->pwm);
168 return 0;
169}
170#endif
171
172static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume);
173
174static struct of_device_id of_pwm_fan_match[] = {
175 { .compatible = "pwm-fan", },
176 {},
177};
178
179static struct platform_driver pwm_fan_driver = {
180 .probe = pwm_fan_probe,
181 .remove = pwm_fan_remove,
182 .driver = {
183 .name = "pwm-fan",
184 .pm = &pwm_fan_pm,
185 .of_match_table = of_pwm_fan_match,
186 },
187};
188
189module_platform_driver(pwm_fan_driver);
190
191MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
192MODULE_ALIAS("platform:pwm-fan");
193MODULE_DESCRIPTION("PWM FAN driver");
194MODULE_LICENSE("GPL");