aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pwm
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 /drivers/pwm
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>
Diffstat (limited to 'drivers/pwm')
-rw-r--r--drivers/pwm/Kconfig12
-rw-r--r--drivers/pwm/Makefile1
-rw-r--r--drivers/pwm/core.c227
3 files changed, 240 insertions, 0 deletions
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);