aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2011-01-28 03:40:40 -0500
committerThierry Reding <thierry.reding@avionic-design.de>2012-06-15 06:56:50 -0400
commit0c2498f1660878339350bea8d18550b1b87ca055 (patch)
treea1509cfa2de90c8a35be4594af5daa79896f7662
parentcfaf025112d3856637ff34a767ef785ef5cf2ca9 (diff)
pwm: Add PWM framework support
This patch adds framework support for PWM (pulse width modulation) devices. The is a barebone PWM API already in the kernel under include/linux/pwm.h, but it does not allow for multiple drivers as each of them implements the pwm_*() functions. There are other PWM framework patches around from Bill Gatliff. Unlike his framework this one does not change the existing API for PWMs so that this framework can act as a drop in replacement for the existing API. Why another framework? Several people argue that there should not be another framework for PWMs but they should be integrated into one of the existing frameworks like led or hwmon. Unlike these frameworks the PWM framework is agnostic to the purpose of the PWM. In fact, a PWM can drive a LED, but this makes the LED framework a user of a PWM, like already done in leds-pwm.c. The gpio framework also is not suitable for PWMs. Every gpio could be turned into a PWM using timer based toggling, but on the other hand not every PWM hardware device can be turned into a gpio due to the lack of hardware capabilities. This patch does not try to improve the PWM API yet, this could be done in subsequent patches. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Acked-by: Kurt Van Dijck <kurt.van.dijck@eia.be> Reviewed-by: Arnd Bergmann <arnd@arndb.de> Reviewed-by: Matthias Kaehlcke <matthias@kaehlcke.net> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Reviewed-by: Shawn Guo <shawn.guo@linaro.org> [thierry.reding@avionic-design.de: fixup typos, kerneldoc comments] Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
-rw-r--r--Documentation/pwm.txt54
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/pwm/Kconfig12
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/core.c227
-rw-r--r--include/linux/pwm.h38
8 files changed, 341 insertions, 0 deletions
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
new file mode 100644
index 000000000000..03e39d145911
--- /dev/null
+++ b/Documentation/pwm.txt
@@ -0,0 +1,54 @@
1Pulse Width Modulation (PWM) interface
2
3This provides an overview about the Linux PWM interface
4
5PWMs are commonly used for controlling LEDs, fans or vibrators in
6cell phones. PWMs with a fixed purpose have no need implementing
7the Linux PWM API (although they could). However, PWMs are often
8found as discrete devices on SoCs which have no fixed purpose. It's
9up to the board designer to connect them to LEDs or fans. To provide
10this kind of flexibility the generic PWM API exists.
11
12Identifying PWMs
13----------------
14
15Users of the legacy PWM API use unique IDs to refer to PWM devices. One
16goal of the new PWM framework is to get rid of this global namespace.
17
18Using PWMs
19----------
20
21A PWM can be requested using pwm_request() and freed after usage with
22pwm_free(). After being requested a PWM has to be configured using
23
24int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);
25
26To start/stop toggling the PWM output use pwm_enable()/pwm_disable().
27
28Implementing a PWM driver
29-------------------------
30
31Currently there are two ways to implement pwm drivers. Traditionally
32there only has been the barebone API meaning that each driver has
33to implement the pwm_*() functions itself. This means that it's impossible
34to have multiple PWM drivers in the system. For this reason it's mandatory
35for new drivers to use the generic PWM framework.
36A new PWM device can be added using pwmchip_add() and removed again with
37pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as
38argument which provides the ops and the pwm id to the framework.
39
40Locking
41-------
42
43The PWM core list manipulations are protected by a mutex, so pwm_request()
44and pwm_free() may not be called from an atomic context. Currently the
45PWM core does not enforce any locking to pwm_enable(), pwm_disable() and
46pwm_config(), so the calling context is currently driver specific. This
47is an issue derived from the former barebone API and should be fixed soon.
48
49Helpers
50-------
51
52Currently a PWM can only be configured with period_ns and duty_ns. For several
53use cases freq_hz and duty_percent might be better. Instead of calculating
54this in your driver please consider adding appropriate helpers to the framework.
diff --git a/MAINTAINERS b/MAINTAINERS
index 14bc7071f9df..67d6cb70cb7e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5487,6 +5487,12 @@ S: Maintained
5487F: Documentation/video4linux/README.pvrusb2 5487F: Documentation/video4linux/README.pvrusb2
5488F: drivers/media/video/pvrusb2/ 5488F: drivers/media/video/pvrusb2/
5489 5489
5490PWM core
5491M: Sascha Hauer <s.hauer@pengutronix.de>
5492L: linux-kernel@vger.kernel.org
5493S: Maintained
5494F: drivers/pwm/
5495
5490PXA2xx/PXA3xx SUPPORT 5496PXA2xx/PXA3xx SUPPORT
5491M: Eric Miao <eric.y.miao@gmail.com> 5497M: Eric Miao <eric.y.miao@gmail.com>
5492M: Russell King <linux@arm.linux.org.uk> 5498M: Russell King <linux@arm.linux.org.uk>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index bfc918633fd9..805c432c9439 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -148,4 +148,6 @@ source "drivers/iio/Kconfig"
148 148
149source "drivers/vme/Kconfig" 149source "drivers/vme/Kconfig"
150 150
151source "drivers/pwm/Kconfig"
152
151endmenu 153endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 2ba29ffef2cb..bd36f09f2246 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -8,6 +8,7 @@
8# GPIO must come after pinctrl as gpios may need to mux pins etc 8# GPIO must come after pinctrl as gpios may need to mux pins etc
9obj-y += pinctrl/ 9obj-y += pinctrl/
10obj-y += gpio/ 10obj-y += gpio/
11obj-y += pwm/
11obj-$(CONFIG_PCI) += pci/ 12obj-$(CONFIG_PCI) += pci/
12obj-$(CONFIG_PARISC) += parisc/ 13obj-$(CONFIG_PARISC) += parisc/
13obj-$(CONFIG_RAPIDIO) += rapidio/ 14obj-$(CONFIG_RAPIDIO) += rapidio/
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
new file mode 100644
index 000000000000..93c1052291a3
--- /dev/null
+++ b/drivers/pwm/Kconfig
@@ -0,0 +1,12 @@
1menuconfig PWM
2 bool "PWM Support"
3 help
4 This enables PWM support through the generic PWM framework.
5 You only need to enable this, if you also want to enable
6 one or more of the PWM drivers below.
7
8 If unsure, say N.
9
10if PWM
11
12endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
new file mode 100644
index 000000000000..3469c3d28b7a
--- /dev/null
+++ b/drivers/pwm/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_PWM) += core.o
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
new file mode 100644
index 000000000000..0b8a38eca23c
--- /dev/null
+++ b/drivers/pwm/core.c
@@ -0,0 +1,227 @@
1/*
2 * Generic pwmlib implementation
3 *
4 * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; see the file COPYING. If not, write to
18 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21#include <linux/module.h>
22#include <linux/pwm.h>
23#include <linux/list.h>
24#include <linux/mutex.h>
25#include <linux/err.h>
26#include <linux/slab.h>
27#include <linux/device.h>
28
29struct pwm_device {
30 struct pwm_chip *chip;
31 const char *label;
32 unsigned long flags;
33#define FLAG_REQUESTED 0
34#define FLAG_ENABLED 1
35 struct list_head node;
36};
37
38static LIST_HEAD(pwm_list);
39
40static DEFINE_MUTEX(pwm_lock);
41
42static struct pwm_device *_find_pwm(int pwm_id)
43{
44 struct pwm_device *pwm;
45
46 list_for_each_entry(pwm, &pwm_list, node) {
47 if (pwm->chip->pwm_id == pwm_id)
48 return pwm;
49 }
50
51 return NULL;
52}
53
54/**
55 * pwmchip_add() - register a new PWM chip
56 * @chip: the PWM chip to add
57 */
58int pwmchip_add(struct pwm_chip *chip)
59{
60 struct pwm_device *pwm;
61 int ret = 0;
62
63 pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
64 if (!pwm)
65 return -ENOMEM;
66
67 pwm->chip = chip;
68
69 mutex_lock(&pwm_lock);
70
71 if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) {
72 ret = -EBUSY;
73 goto out;
74 }
75
76 list_add_tail(&pwm->node, &pwm_list);
77out:
78 mutex_unlock(&pwm_lock);
79
80 if (ret)
81 kfree(pwm);
82
83 return ret;
84}
85EXPORT_SYMBOL_GPL(pwmchip_add);
86
87/**
88 * pwmchip_remove() - remove a PWM chip
89 * @chip: the PWM chip to remove
90 *
91 * Removes a PWM chip. This function may return busy if the PWM chip provides
92 * a PWM device that is still requested.
93 */
94int pwmchip_remove(struct pwm_chip *chip)
95{
96 struct pwm_device *pwm;
97 int ret = 0;
98
99 mutex_lock(&pwm_lock);
100
101 pwm = _find_pwm(chip->pwm_id);
102 if (!pwm) {
103 ret = -ENOENT;
104 goto out;
105 }
106
107 if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
108 ret = -EBUSY;
109 goto out;
110 }
111
112 list_del(&pwm->node);
113
114 kfree(pwm);
115out:
116 mutex_unlock(&pwm_lock);
117
118 return ret;
119}
120EXPORT_SYMBOL_GPL(pwmchip_remove);
121
122/**
123 * pwm_request() - request a PWM device
124 * @pwm_id: global PWM device index
125 * @label: PWM device label
126 */
127struct pwm_device *pwm_request(int pwm_id, const char *label)
128{
129 struct pwm_device *pwm;
130 int ret;
131
132 mutex_lock(&pwm_lock);
133
134 pwm = _find_pwm(pwm_id);
135 if (!pwm) {
136 pwm = ERR_PTR(-ENOENT);
137 goto out;
138 }
139
140 if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
141 pwm = ERR_PTR(-EBUSY);
142 goto out;
143 }
144
145 if (!try_module_get(pwm->chip->ops->owner)) {
146 pwm = ERR_PTR(-ENODEV);
147 goto out;
148 }
149
150 if (pwm->chip->ops->request) {
151 ret = pwm->chip->ops->request(pwm->chip);
152 if (ret) {
153 pwm = ERR_PTR(ret);
154 goto out_put;
155 }
156 }
157
158 pwm->label = label;
159 set_bit(FLAG_REQUESTED, &pwm->flags);
160
161 goto out;
162
163out_put:
164 module_put(pwm->chip->ops->owner);
165out:
166 mutex_unlock(&pwm_lock);
167
168 return pwm;
169}
170EXPORT_SYMBOL_GPL(pwm_request);
171
172/**
173 * pwm_free() - free a PWM device
174 * @pwm: PWM device
175 */
176void pwm_free(struct pwm_device *pwm)
177{
178 mutex_lock(&pwm_lock);
179
180 if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) {
181 pr_warning("PWM device already freed\n");
182 goto out;
183 }
184
185 pwm->label = NULL;
186
187 module_put(pwm->chip->ops->owner);
188out:
189 mutex_unlock(&pwm_lock);
190}
191EXPORT_SYMBOL_GPL(pwm_free);
192
193/**
194 * pwm_config() - change a PWM device configuration
195 * @pwm: PWM device
196 * @duty_ns: "on" time (in nanoseconds)
197 * @period_ns: duration (in nanoseconds) of one cycle
198 */
199int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
200{
201 return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns);
202}
203EXPORT_SYMBOL_GPL(pwm_config);
204
205/**
206 * pwm_enable() - start a PWM output toggling
207 * @pwm: PWM device
208 */
209int pwm_enable(struct pwm_device *pwm)
210{
211 if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags))
212 return pwm->chip->ops->enable(pwm->chip);
213
214 return 0;
215}
216EXPORT_SYMBOL_GPL(pwm_enable);
217
218/**
219 * pwm_disable() - stop a PWM output toggling
220 * @pwm: PWM device
221 */
222void pwm_disable(struct pwm_device *pwm)
223{
224 if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags))
225 pwm->chip->ops->disable(pwm->chip);
226}
227EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 7c775751392c..1f308a13105f 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -28,4 +28,42 @@ int pwm_enable(struct pwm_device *pwm);
28 */ 28 */
29void pwm_disable(struct pwm_device *pwm); 29void pwm_disable(struct pwm_device *pwm);
30 30
31#ifdef CONFIG_PWM
32struct pwm_chip;
33
34/**
35 * struct pwm_ops - PWM controller operations
36 * @request: optional hook for requesting a PWM
37 * @free: optional hook for freeing a PWM
38 * @config: configure duty cycles and period length for this PWM
39 * @enable: enable PWM output toggling
40 * @disable: disable PWM output toggling
41 * @owner: helps prevent removal of modules exporting active PWMs
42 */
43struct pwm_ops {
44 int (*request)(struct pwm_chip *chip);
45 void (*free)(struct pwm_chip *chip);
46 int (*config)(struct pwm_chip *chip, int duty_ns,
47 int period_ns);
48 int (*enable)(struct pwm_chip *chip);
49 void (*disable)(struct pwm_chip *chip);
50 struct module *owner;
51};
52
53/**
54 * struct pwm_chip - abstract a PWM
55 * @pwm_id: global PWM device index
56 * @label: PWM device label
57 * @ops: controller operations
58 */
59struct pwm_chip {
60 int pwm_id;
61 const char *label;
62 struct pwm_ops *ops;
63};
64
65int pwmchip_add(struct pwm_chip *chip);
66int pwmchip_remove(struct pwm_chip *chip);
67#endif
68
31#endif /* __LINUX_PWM_H */ 69#endif /* __LINUX_PWM_H */