diff options
author | Thierry Reding <thierry.reding@avionic-design.de> | 2011-12-14 05:12:23 -0500 |
---|---|---|
committer | Thierry Reding <thierry.reding@avionic-design.de> | 2012-06-15 06:56:52 -0400 |
commit | f051c466cf690ac661d713d3ceb56b4efcecc853 (patch) | |
tree | 15291a08d3ec44e8ab81c44370e1ac2bbb366ec5 /drivers/pwm/core.c | |
parent | 0c2498f1660878339350bea8d18550b1b87ca055 (diff) |
pwm: Allow chips to support multiple PWMs
Many PWM controllers provide access to more than a single PWM output and
may even share some resource among them. Allowing a PWM chip to provide
multiple PWM devices enables better sharing of those resources. As a
side-effect this change allows easy integration with the device tree
where a given PWM can be looked up based on the PWM chip's phandle and a
corresponding index.
This commit modifies the PWM core to support multiple PWMs per struct
pwm_chip. It achieves this in a similar way to how gpiolib works, by
allowing PWM ranges to be requested dynamically (pwm_chip.base == -1) or
starting at a given offset (pwm_chip.base >= 0). A chip specifies how
many PWMs it controls using the npwm member. Each of the functions in
the pwm_ops structure gets an additional argument that specified the PWM
number (it can be converted to a per-chip index by subtracting the
chip's base).
The total maximum number of PWM devices is currently fixed to 1024 while
the data is actually stored in a radix tree, thus saving resources if
not all of them are used.
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Reviewed-by: Shawn Guo <shawn.guo@linaro.org>
[eric@eukrea.com: fix error handling in pwmchip_add]
Signed-off-by: Eric BĂ©nard <eric@eukrea.com>
Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
Diffstat (limited to 'drivers/pwm/core.c')
-rw-r--r-- | drivers/pwm/core.c | 267 |
1 files changed, 190 insertions, 77 deletions
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 0b8a38eca23c..a447be128328 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Generic pwmlib implementation | 2 | * Generic pwmlib implementation |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de> | 4 | * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de> |
5 | * Copyright (C) 2011-2012 Avionic Design GmbH | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * 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 | * it under the terms of the GNU General Public License as published by |
@@ -20,66 +21,161 @@ | |||
20 | 21 | ||
21 | #include <linux/module.h> | 22 | #include <linux/module.h> |
22 | #include <linux/pwm.h> | 23 | #include <linux/pwm.h> |
24 | #include <linux/radix-tree.h> | ||
23 | #include <linux/list.h> | 25 | #include <linux/list.h> |
24 | #include <linux/mutex.h> | 26 | #include <linux/mutex.h> |
25 | #include <linux/err.h> | 27 | #include <linux/err.h> |
26 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
27 | #include <linux/device.h> | 29 | #include <linux/device.h> |
28 | 30 | ||
29 | struct pwm_device { | 31 | #define MAX_PWMS 1024 |
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 | 32 | ||
40 | static DEFINE_MUTEX(pwm_lock); | 33 | static DEFINE_MUTEX(pwm_lock); |
34 | static LIST_HEAD(pwm_chips); | ||
35 | static DECLARE_BITMAP(allocated_pwms, MAX_PWMS); | ||
36 | static RADIX_TREE(pwm_tree, GFP_KERNEL); | ||
41 | 37 | ||
42 | static struct pwm_device *_find_pwm(int pwm_id) | 38 | static struct pwm_device *pwm_to_device(unsigned int pwm) |
43 | { | 39 | { |
44 | struct pwm_device *pwm; | 40 | return radix_tree_lookup(&pwm_tree, pwm); |
41 | } | ||
42 | |||
43 | static int alloc_pwms(int pwm, unsigned int count) | ||
44 | { | ||
45 | unsigned int from = 0; | ||
46 | unsigned int start; | ||
47 | |||
48 | if (pwm >= MAX_PWMS) | ||
49 | return -EINVAL; | ||
50 | |||
51 | if (pwm >= 0) | ||
52 | from = pwm; | ||
45 | 53 | ||
46 | list_for_each_entry(pwm, &pwm_list, node) { | 54 | start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from, |
47 | if (pwm->chip->pwm_id == pwm_id) | 55 | count, 0); |
48 | return pwm; | 56 | |
57 | if (pwm >= 0 && start != pwm) | ||
58 | return -EEXIST; | ||
59 | |||
60 | if (start + count > MAX_PWMS) | ||
61 | return -ENOSPC; | ||
62 | |||
63 | return start; | ||
64 | } | ||
65 | |||
66 | static void free_pwms(struct pwm_chip *chip) | ||
67 | { | ||
68 | unsigned int i; | ||
69 | |||
70 | for (i = 0; i < chip->npwm; i++) { | ||
71 | struct pwm_device *pwm = &chip->pwms[i]; | ||
72 | radix_tree_delete(&pwm_tree, pwm->pwm); | ||
49 | } | 73 | } |
50 | 74 | ||
51 | return NULL; | 75 | bitmap_clear(allocated_pwms, chip->base, chip->npwm); |
76 | |||
77 | kfree(chip->pwms); | ||
78 | chip->pwms = NULL; | ||
79 | } | ||
80 | |||
81 | static int pwm_device_request(struct pwm_device *pwm, const char *label) | ||
82 | { | ||
83 | int err; | ||
84 | |||
85 | if (test_bit(PWMF_REQUESTED, &pwm->flags)) | ||
86 | return -EBUSY; | ||
87 | |||
88 | if (!try_module_get(pwm->chip->ops->owner)) | ||
89 | return -ENODEV; | ||
90 | |||
91 | if (pwm->chip->ops->request) { | ||
92 | err = pwm->chip->ops->request(pwm->chip, pwm); | ||
93 | if (err) { | ||
94 | module_put(pwm->chip->ops->owner); | ||
95 | return err; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | set_bit(PWMF_REQUESTED, &pwm->flags); | ||
100 | pwm->label = label; | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * pwm_set_chip_data() - set private chip data for a PWM | ||
107 | * @pwm: PWM device | ||
108 | * @data: pointer to chip-specific data | ||
109 | */ | ||
110 | int pwm_set_chip_data(struct pwm_device *pwm, void *data) | ||
111 | { | ||
112 | if (!pwm) | ||
113 | return -EINVAL; | ||
114 | |||
115 | pwm->chip_data = data; | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * pwm_get_chip_data() - get private chip data for a PWM | ||
122 | * @pwm: PWM device | ||
123 | */ | ||
124 | void *pwm_get_chip_data(struct pwm_device *pwm) | ||
125 | { | ||
126 | return pwm ? pwm->chip_data : NULL; | ||
52 | } | 127 | } |
53 | 128 | ||
54 | /** | 129 | /** |
55 | * pwmchip_add() - register a new PWM chip | 130 | * pwmchip_add() - register a new PWM chip |
56 | * @chip: the PWM chip to add | 131 | * @chip: the PWM chip to add |
132 | * | ||
133 | * Register a new PWM chip. If chip->base < 0 then a dynamically assigned base | ||
134 | * will be used. | ||
57 | */ | 135 | */ |
58 | int pwmchip_add(struct pwm_chip *chip) | 136 | int pwmchip_add(struct pwm_chip *chip) |
59 | { | 137 | { |
60 | struct pwm_device *pwm; | 138 | struct pwm_device *pwm; |
61 | int ret = 0; | 139 | unsigned int i; |
62 | 140 | int ret; | |
63 | pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); | ||
64 | if (!pwm) | ||
65 | return -ENOMEM; | ||
66 | 141 | ||
67 | pwm->chip = chip; | 142 | if (!chip || !chip->dev || !chip->ops || !chip->ops->config || |
143 | !chip->ops->enable || !chip->ops->disable) | ||
144 | return -EINVAL; | ||
68 | 145 | ||
69 | mutex_lock(&pwm_lock); | 146 | mutex_lock(&pwm_lock); |
70 | 147 | ||
71 | if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) { | 148 | ret = alloc_pwms(chip->base, chip->npwm); |
72 | ret = -EBUSY; | 149 | if (ret < 0) |
150 | goto out; | ||
151 | |||
152 | chip->pwms = kzalloc(chip->npwm * sizeof(*pwm), GFP_KERNEL); | ||
153 | if (!chip->pwms) { | ||
154 | ret = -ENOMEM; | ||
73 | goto out; | 155 | goto out; |
74 | } | 156 | } |
75 | 157 | ||
76 | list_add_tail(&pwm->node, &pwm_list); | 158 | chip->base = ret; |
77 | out: | 159 | |
78 | mutex_unlock(&pwm_lock); | 160 | for (i = 0; i < chip->npwm; i++) { |
161 | pwm = &chip->pwms[i]; | ||
162 | |||
163 | pwm->chip = chip; | ||
164 | pwm->pwm = chip->base + i; | ||
165 | pwm->hwpwm = i; | ||
79 | 166 | ||
80 | if (ret) | 167 | radix_tree_insert(&pwm_tree, pwm->pwm, pwm); |
81 | kfree(pwm); | 168 | } |
169 | |||
170 | bitmap_set(allocated_pwms, chip->base, chip->npwm); | ||
171 | |||
172 | INIT_LIST_HEAD(&chip->list); | ||
173 | list_add(&chip->list, &pwm_chips); | ||
82 | 174 | ||
175 | ret = 0; | ||
176 | |||
177 | out: | ||
178 | mutex_unlock(&pwm_lock); | ||
83 | return ret; | 179 | return ret; |
84 | } | 180 | } |
85 | EXPORT_SYMBOL_GPL(pwmchip_add); | 181 | EXPORT_SYMBOL_GPL(pwmchip_add); |
@@ -93,28 +189,25 @@ EXPORT_SYMBOL_GPL(pwmchip_add); | |||
93 | */ | 189 | */ |
94 | int pwmchip_remove(struct pwm_chip *chip) | 190 | int pwmchip_remove(struct pwm_chip *chip) |
95 | { | 191 | { |
96 | struct pwm_device *pwm; | 192 | unsigned int i; |
97 | int ret = 0; | 193 | int ret = 0; |
98 | 194 | ||
99 | mutex_lock(&pwm_lock); | 195 | mutex_lock(&pwm_lock); |
100 | 196 | ||
101 | pwm = _find_pwm(chip->pwm_id); | 197 | for (i = 0; i < chip->npwm; i++) { |
102 | if (!pwm) { | 198 | struct pwm_device *pwm = &chip->pwms[i]; |
103 | ret = -ENOENT; | ||
104 | goto out; | ||
105 | } | ||
106 | 199 | ||
107 | if (test_bit(FLAG_REQUESTED, &pwm->flags)) { | 200 | if (test_bit(PWMF_REQUESTED, &pwm->flags)) { |
108 | ret = -EBUSY; | 201 | ret = -EBUSY; |
109 | goto out; | 202 | goto out; |
203 | } | ||
110 | } | 204 | } |
111 | 205 | ||
112 | list_del(&pwm->node); | 206 | list_del_init(&chip->list); |
207 | free_pwms(chip); | ||
113 | 208 | ||
114 | kfree(pwm); | ||
115 | out: | 209 | out: |
116 | mutex_unlock(&pwm_lock); | 210 | mutex_unlock(&pwm_lock); |
117 | |||
118 | return ret; | 211 | return ret; |
119 | } | 212 | } |
120 | EXPORT_SYMBOL_GPL(pwmchip_remove); | 213 | EXPORT_SYMBOL_GPL(pwmchip_remove); |
@@ -124,50 +217,64 @@ EXPORT_SYMBOL_GPL(pwmchip_remove); | |||
124 | * @pwm_id: global PWM device index | 217 | * @pwm_id: global PWM device index |
125 | * @label: PWM device label | 218 | * @label: PWM device label |
126 | */ | 219 | */ |
127 | struct pwm_device *pwm_request(int pwm_id, const char *label) | 220 | struct pwm_device *pwm_request(int pwm, const char *label) |
128 | { | 221 | { |
129 | struct pwm_device *pwm; | 222 | struct pwm_device *dev; |
130 | int ret; | 223 | int err; |
224 | |||
225 | if (pwm < 0 || pwm >= MAX_PWMS) | ||
226 | return ERR_PTR(-EINVAL); | ||
131 | 227 | ||
132 | mutex_lock(&pwm_lock); | 228 | mutex_lock(&pwm_lock); |
133 | 229 | ||
134 | pwm = _find_pwm(pwm_id); | 230 | dev = pwm_to_device(pwm); |
135 | if (!pwm) { | 231 | if (!dev) { |
136 | pwm = ERR_PTR(-ENOENT); | 232 | dev = ERR_PTR(-EPROBE_DEFER); |
137 | goto out; | 233 | goto out; |
138 | } | 234 | } |
139 | 235 | ||
140 | if (test_bit(FLAG_REQUESTED, &pwm->flags)) { | 236 | err = pwm_device_request(dev, label); |
141 | pwm = ERR_PTR(-EBUSY); | 237 | if (err < 0) |
142 | goto out; | 238 | dev = ERR_PTR(err); |
143 | } | ||
144 | 239 | ||
145 | if (!try_module_get(pwm->chip->ops->owner)) { | 240 | out: |
146 | pwm = ERR_PTR(-ENODEV); | 241 | mutex_unlock(&pwm_lock); |
147 | goto out; | ||
148 | } | ||
149 | 242 | ||
150 | if (pwm->chip->ops->request) { | 243 | return dev; |
151 | ret = pwm->chip->ops->request(pwm->chip); | 244 | } |
152 | if (ret) { | 245 | EXPORT_SYMBOL_GPL(pwm_request); |
153 | pwm = ERR_PTR(ret); | ||
154 | goto out_put; | ||
155 | } | ||
156 | } | ||
157 | 246 | ||
158 | pwm->label = label; | 247 | /** |
159 | set_bit(FLAG_REQUESTED, &pwm->flags); | 248 | * pwm_request_from_chip() - request a PWM device relative to a PWM chip |
249 | * @chip: PWM chip | ||
250 | * @index: per-chip index of the PWM to request | ||
251 | * @label: a literal description string of this PWM | ||
252 | * | ||
253 | * Returns the PWM at the given index of the given PWM chip. A negative error | ||
254 | * code is returned if the index is not valid for the specified PWM chip or | ||
255 | * if the PWM device cannot be requested. | ||
256 | */ | ||
257 | struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, | ||
258 | unsigned int index, | ||
259 | const char *label) | ||
260 | { | ||
261 | struct pwm_device *pwm; | ||
262 | int err; | ||
160 | 263 | ||
161 | goto out; | 264 | if (!chip || index >= chip->npwm) |
265 | return ERR_PTR(-EINVAL); | ||
162 | 266 | ||
163 | out_put: | 267 | mutex_lock(&pwm_lock); |
164 | module_put(pwm->chip->ops->owner); | 268 | pwm = &chip->pwms[index]; |
165 | out: | ||
166 | mutex_unlock(&pwm_lock); | ||
167 | 269 | ||
270 | err = pwm_device_request(pwm, label); | ||
271 | if (err < 0) | ||
272 | pwm = ERR_PTR(err); | ||
273 | |||
274 | mutex_unlock(&pwm_lock); | ||
168 | return pwm; | 275 | return pwm; |
169 | } | 276 | } |
170 | EXPORT_SYMBOL_GPL(pwm_request); | 277 | EXPORT_SYMBOL_GPL(pwm_request_from_chip); |
171 | 278 | ||
172 | /** | 279 | /** |
173 | * pwm_free() - free a PWM device | 280 | * pwm_free() - free a PWM device |
@@ -177,11 +284,14 @@ void pwm_free(struct pwm_device *pwm) | |||
177 | { | 284 | { |
178 | mutex_lock(&pwm_lock); | 285 | mutex_lock(&pwm_lock); |
179 | 286 | ||
180 | if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) { | 287 | if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) { |
181 | pr_warning("PWM device already freed\n"); | 288 | pr_warning("PWM device already freed\n"); |
182 | goto out; | 289 | goto out; |
183 | } | 290 | } |
184 | 291 | ||
292 | if (pwm->chip->ops->free) | ||
293 | pwm->chip->ops->free(pwm->chip, pwm); | ||
294 | |||
185 | pwm->label = NULL; | 295 | pwm->label = NULL; |
186 | 296 | ||
187 | module_put(pwm->chip->ops->owner); | 297 | module_put(pwm->chip->ops->owner); |
@@ -198,7 +308,10 @@ EXPORT_SYMBOL_GPL(pwm_free); | |||
198 | */ | 308 | */ |
199 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) | 309 | int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) |
200 | { | 310 | { |
201 | return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns); | 311 | if (!pwm || period_ns == 0 || duty_ns > period_ns) |
312 | return -EINVAL; | ||
313 | |||
314 | return pwm->chip->ops->config(pwm->chip, pwm, duty_ns, period_ns); | ||
202 | } | 315 | } |
203 | EXPORT_SYMBOL_GPL(pwm_config); | 316 | EXPORT_SYMBOL_GPL(pwm_config); |
204 | 317 | ||
@@ -208,10 +321,10 @@ EXPORT_SYMBOL_GPL(pwm_config); | |||
208 | */ | 321 | */ |
209 | int pwm_enable(struct pwm_device *pwm) | 322 | int pwm_enable(struct pwm_device *pwm) |
210 | { | 323 | { |
211 | if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags)) | 324 | if (pwm && !test_and_set_bit(PWMF_ENABLED, &pwm->flags)) |
212 | return pwm->chip->ops->enable(pwm->chip); | 325 | return pwm->chip->ops->enable(pwm->chip, pwm); |
213 | 326 | ||
214 | return 0; | 327 | return pwm ? 0 : -EINVAL; |
215 | } | 328 | } |
216 | EXPORT_SYMBOL_GPL(pwm_enable); | 329 | EXPORT_SYMBOL_GPL(pwm_enable); |
217 | 330 | ||
@@ -221,7 +334,7 @@ EXPORT_SYMBOL_GPL(pwm_enable); | |||
221 | */ | 334 | */ |
222 | void pwm_disable(struct pwm_device *pwm) | 335 | void pwm_disable(struct pwm_device *pwm) |
223 | { | 336 | { |
224 | if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags)) | 337 | if (pwm && test_and_clear_bit(PWMF_ENABLED, &pwm->flags)) |
225 | pwm->chip->ops->disable(pwm->chip); | 338 | pwm->chip->ops->disable(pwm->chip, pwm); |
226 | } | 339 | } |
227 | EXPORT_SYMBOL_GPL(pwm_disable); | 340 | EXPORT_SYMBOL_GPL(pwm_disable); |