diff options
Diffstat (limited to 'drivers/pwm')
-rw-r--r-- | drivers/pwm/Kconfig | 12 | ||||
-rw-r--r-- | drivers/pwm/Makefile | 1 | ||||
-rw-r--r-- | drivers/pwm/core.c | 227 |
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 @@ | |||
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 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 | |||
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); | ||