diff options
-rw-r--r-- | Documentation/pwm.txt | 54 | ||||
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 12 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/core.c | 227 | ||||
-rw-r--r-- | include/linux/pwm.h | 38 |
8 files changed, 341 insertions, 0 deletions
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt new file mode 100644 index 00000000000..03e39d14591 --- /dev/null +++ b/Documentation/pwm.txt | |||
@@ -0,0 +1,54 @@ | |||
1 | Pulse Width Modulation (PWM) interface | ||
2 | |||
3 | This provides an overview about the Linux PWM interface | ||
4 | |||
5 | PWMs are commonly used for controlling LEDs, fans or vibrators in | ||
6 | cell phones. PWMs with a fixed purpose have no need implementing | ||
7 | the Linux PWM API (although they could). However, PWMs are often | ||
8 | found as discrete devices on SoCs which have no fixed purpose. It's | ||
9 | up to the board designer to connect them to LEDs or fans. To provide | ||
10 | this kind of flexibility the generic PWM API exists. | ||
11 | |||
12 | Identifying PWMs | ||
13 | ---------------- | ||
14 | |||
15 | Users of the legacy PWM API use unique IDs to refer to PWM devices. One | ||
16 | goal of the new PWM framework is to get rid of this global namespace. | ||
17 | |||
18 | Using PWMs | ||
19 | ---------- | ||
20 | |||
21 | A PWM can be requested using pwm_request() and freed after usage with | ||
22 | pwm_free(). After being requested a PWM has to be configured using | ||
23 | |||
24 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); | ||
25 | |||
26 | To start/stop toggling the PWM output use pwm_enable()/pwm_disable(). | ||
27 | |||
28 | Implementing a PWM driver | ||
29 | ------------------------- | ||
30 | |||
31 | Currently there are two ways to implement pwm drivers. Traditionally | ||
32 | there only has been the barebone API meaning that each driver has | ||
33 | to implement the pwm_*() functions itself. This means that it's impossible | ||
34 | to have multiple PWM drivers in the system. For this reason it's mandatory | ||
35 | for new drivers to use the generic PWM framework. | ||
36 | A new PWM device can be added using pwmchip_add() and removed again with | ||
37 | pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as | ||
38 | argument which provides the ops and the pwm id to the framework. | ||
39 | |||
40 | Locking | ||
41 | ------- | ||
42 | |||
43 | The PWM core list manipulations are protected by a mutex, so pwm_request() | ||
44 | and pwm_free() may not be called from an atomic context. Currently the | ||
45 | PWM core does not enforce any locking to pwm_enable(), pwm_disable() and | ||
46 | pwm_config(), so the calling context is currently driver specific. This | ||
47 | is an issue derived from the former barebone API and should be fixed soon. | ||
48 | |||
49 | Helpers | ||
50 | ------- | ||
51 | |||
52 | Currently a PWM can only be configured with period_ns and duty_ns. For several | ||
53 | use cases freq_hz and duty_percent might be better. Instead of calculating | ||
54 | this in your driver please consider adding appropriate helpers to the framework. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 14bc7071f9d..67d6cb70cb7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -5487,6 +5487,12 @@ S: Maintained | |||
5487 | F: Documentation/video4linux/README.pvrusb2 | 5487 | F: Documentation/video4linux/README.pvrusb2 |
5488 | F: drivers/media/video/pvrusb2/ | 5488 | F: drivers/media/video/pvrusb2/ |
5489 | 5489 | ||
5490 | PWM core | ||
5491 | M: Sascha Hauer <s.hauer@pengutronix.de> | ||
5492 | L: linux-kernel@vger.kernel.org | ||
5493 | S: Maintained | ||
5494 | F: drivers/pwm/ | ||
5495 | |||
5490 | PXA2xx/PXA3xx SUPPORT | 5496 | PXA2xx/PXA3xx SUPPORT |
5491 | M: Eric Miao <eric.y.miao@gmail.com> | 5497 | M: Eric Miao <eric.y.miao@gmail.com> |
5492 | M: Russell King <linux@arm.linux.org.uk> | 5498 | M: Russell King <linux@arm.linux.org.uk> |
diff --git a/drivers/Kconfig b/drivers/Kconfig index bfc918633fd..805c432c943 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig | |||
@@ -148,4 +148,6 @@ source "drivers/iio/Kconfig" | |||
148 | 148 | ||
149 | source "drivers/vme/Kconfig" | 149 | source "drivers/vme/Kconfig" |
150 | 150 | ||
151 | source "drivers/pwm/Kconfig" | ||
152 | |||
151 | endmenu | 153 | endmenu |
diff --git a/drivers/Makefile b/drivers/Makefile index 2ba29ffef2c..bd36f09f224 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 |
9 | obj-y += pinctrl/ | 9 | obj-y += pinctrl/ |
10 | obj-y += gpio/ | 10 | obj-y += gpio/ |
11 | obj-y += pwm/ | ||
11 | obj-$(CONFIG_PCI) += pci/ | 12 | obj-$(CONFIG_PCI) += pci/ |
12 | obj-$(CONFIG_PARISC) += parisc/ | 13 | obj-$(CONFIG_PARISC) += parisc/ |
13 | obj-$(CONFIG_RAPIDIO) += rapidio/ | 14 | obj-$(CONFIG_RAPIDIO) += rapidio/ |
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig new file mode 100644 index 00000000000..93c1052291a --- /dev/null +++ b/drivers/pwm/Kconfig | |||
@@ -0,0 +1,12 @@ | |||
1 | menuconfig 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 | |||
10 | if PWM | ||
11 | |||
12 | endif | ||
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile new file mode 100644 index 00000000000..3469c3d28b7 --- /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 00000000000..0b8a38eca23 --- /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 | |||
29 | struct 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 | |||
38 | static LIST_HEAD(pwm_list); | ||
39 | |||
40 | static DEFINE_MUTEX(pwm_lock); | ||
41 | |||
42 | static 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 | */ | ||
58 | int 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); | ||
77 | out: | ||
78 | mutex_unlock(&pwm_lock); | ||
79 | |||
80 | if (ret) | ||
81 | kfree(pwm); | ||
82 | |||
83 | return ret; | ||
84 | } | ||
85 | EXPORT_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 | */ | ||
94 | int 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); | ||
115 | out: | ||
116 | mutex_unlock(&pwm_lock); | ||
117 | |||
118 | return ret; | ||
119 | } | ||
120 | EXPORT_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 | */ | ||
127 | struct 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 | |||
163 | out_put: | ||
164 | module_put(pwm->chip->ops->owner); | ||
165 | out: | ||
166 | mutex_unlock(&pwm_lock); | ||
167 | |||
168 | return pwm; | ||
169 | } | ||
170 | EXPORT_SYMBOL_GPL(pwm_request); | ||
171 | |||
172 | /** | ||
173 | * pwm_free() - free a PWM device | ||
174 | * @pwm: PWM device | ||
175 | */ | ||
176 | void 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); | ||
188 | out: | ||
189 | mutex_unlock(&pwm_lock); | ||
190 | } | ||
191 | EXPORT_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 | */ | ||
199 | int 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 | } | ||
203 | EXPORT_SYMBOL_GPL(pwm_config); | ||
204 | |||
205 | /** | ||
206 | * pwm_enable() - start a PWM output toggling | ||
207 | * @pwm: PWM device | ||
208 | */ | ||
209 | int 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 | } | ||
216 | EXPORT_SYMBOL_GPL(pwm_enable); | ||
217 | |||
218 | /** | ||
219 | * pwm_disable() - stop a PWM output toggling | ||
220 | * @pwm: PWM device | ||
221 | */ | ||
222 | void 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 | } | ||
227 | EXPORT_SYMBOL_GPL(pwm_disable); | ||
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 7c775751392..1f308a13105 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h | |||
@@ -28,4 +28,42 @@ int pwm_enable(struct pwm_device *pwm); | |||
28 | */ | 28 | */ |
29 | void pwm_disable(struct pwm_device *pwm); | 29 | void pwm_disable(struct pwm_device *pwm); |
30 | 30 | ||
31 | #ifdef CONFIG_PWM | ||
32 | struct 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 | */ | ||
43 | struct 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 | */ | ||
59 | struct pwm_chip { | ||
60 | int pwm_id; | ||
61 | const char *label; | ||
62 | struct pwm_ops *ops; | ||
63 | }; | ||
64 | |||
65 | int pwmchip_add(struct pwm_chip *chip); | ||
66 | int pwmchip_remove(struct pwm_chip *chip); | ||
67 | #endif | ||
68 | |||
31 | #endif /* __LINUX_PWM_H */ | 69 | #endif /* __LINUX_PWM_H */ |