aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <thierry.reding@avionic-design.de>2011-12-14 05:12:23 -0500
committerThierry Reding <thierry.reding@avionic-design.de>2012-06-15 06:56:52 -0400
commitf051c466cf690ac661d713d3ceb56b4efcecc853 (patch)
tree15291a08d3ec44e8ab81c44370e1ac2bbb366ec5
parent0c2498f1660878339350bea8d18550b1b87ca055 (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>
-rw-r--r--Documentation/pwm.txt9
-rw-r--r--drivers/pwm/core.c267
-rw-r--r--include/linux/pwm.h71
3 files changed, 254 insertions, 93 deletions
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 03e39d145911..48f598acdd16 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -33,9 +33,12 @@ there only has been the barebone API meaning that each driver has
33to implement the pwm_*() functions itself. This means that it's impossible 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 34to have multiple PWM drivers in the system. For this reason it's mandatory
35for new drivers to use the generic PWM framework. 35for new drivers to use the generic PWM framework.
36A new PWM device can be added using pwmchip_add() and removed again with 36
37pwmchip_remove(). pwmchip_add() takes a filled in struct pwm_chip as 37A new PWM controller/chip can be added using pwmchip_add() and removed
38argument which provides the ops and the pwm id to the framework. 38again with pwmchip_remove(). pwmchip_add() takes a filled in struct
39pwm_chip as argument which provides a description of the PWM chip, the
40number of PWM devices provider by the chip and the chip-specific
41implementation of the supported PWM operations to the framework.
39 42
40Locking 43Locking
41------- 44-------
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
29struct 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
38static LIST_HEAD(pwm_list);
39 32
40static DEFINE_MUTEX(pwm_lock); 33static DEFINE_MUTEX(pwm_lock);
34static LIST_HEAD(pwm_chips);
35static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
36static RADIX_TREE(pwm_tree, GFP_KERNEL);
41 37
42static struct pwm_device *_find_pwm(int pwm_id) 38static 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
43static 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
66static 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
81static 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 */
110int 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 */
124void *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 */
58int pwmchip_add(struct pwm_chip *chip) 136int 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;
77out: 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
177out:
178 mutex_unlock(&pwm_lock);
83 return ret; 179 return ret;
84} 180}
85EXPORT_SYMBOL_GPL(pwmchip_add); 181EXPORT_SYMBOL_GPL(pwmchip_add);
@@ -93,28 +189,25 @@ EXPORT_SYMBOL_GPL(pwmchip_add);
93 */ 189 */
94int pwmchip_remove(struct pwm_chip *chip) 190int 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);
115out: 209out:
116 mutex_unlock(&pwm_lock); 210 mutex_unlock(&pwm_lock);
117
118 return ret; 211 return ret;
119} 212}
120EXPORT_SYMBOL_GPL(pwmchip_remove); 213EXPORT_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 */
127struct pwm_device *pwm_request(int pwm_id, const char *label) 220struct 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)) { 240out:
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) { 245EXPORT_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 */
257struct 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
163out_put: 267 mutex_lock(&pwm_lock);
164 module_put(pwm->chip->ops->owner); 268 pwm = &chip->pwms[index];
165out:
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}
170EXPORT_SYMBOL_GPL(pwm_request); 277EXPORT_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 */
199int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) 309int 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}
203EXPORT_SYMBOL_GPL(pwm_config); 316EXPORT_SYMBOL_GPL(pwm_config);
204 317
@@ -208,10 +321,10 @@ EXPORT_SYMBOL_GPL(pwm_config);
208 */ 321 */
209int pwm_enable(struct pwm_device *pwm) 322int 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}
216EXPORT_SYMBOL_GPL(pwm_enable); 329EXPORT_SYMBOL_GPL(pwm_enable);
217 330
@@ -221,7 +334,7 @@ EXPORT_SYMBOL_GPL(pwm_enable);
221 */ 334 */
222void pwm_disable(struct pwm_device *pwm) 335void 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}
227EXPORT_SYMBOL_GPL(pwm_disable); 340EXPORT_SYMBOL_GPL(pwm_disable);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 1f308a13105f..57103911f4c7 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -31,6 +31,33 @@ void pwm_disable(struct pwm_device *pwm);
31#ifdef CONFIG_PWM 31#ifdef CONFIG_PWM
32struct pwm_chip; 32struct pwm_chip;
33 33
34enum {
35 PWMF_REQUESTED = 1 << 0,
36 PWMF_ENABLED = 1 << 1,
37};
38
39struct pwm_device {
40 const char *label;
41 unsigned long flags;
42 unsigned int hwpwm;
43 unsigned int pwm;
44 struct pwm_chip *chip;
45 void *chip_data;
46
47 unsigned int period; /* in nanoseconds */
48};
49
50static inline void pwm_set_period(struct pwm_device *pwm, unsigned int period)
51{
52 if (pwm)
53 pwm->period = period;
54}
55
56static inline unsigned int pwm_get_period(struct pwm_device *pwm)
57{
58 return pwm ? pwm->period : 0;
59}
60
34/** 61/**
35 * struct pwm_ops - PWM controller operations 62 * struct pwm_ops - PWM controller operations
36 * @request: optional hook for requesting a PWM 63 * @request: optional hook for requesting a PWM
@@ -41,29 +68,47 @@ struct pwm_chip;
41 * @owner: helps prevent removal of modules exporting active PWMs 68 * @owner: helps prevent removal of modules exporting active PWMs
42 */ 69 */
43struct pwm_ops { 70struct pwm_ops {
44 int (*request)(struct pwm_chip *chip); 71 int (*request)(struct pwm_chip *chip,
45 void (*free)(struct pwm_chip *chip); 72 struct pwm_device *pwm);
46 int (*config)(struct pwm_chip *chip, int duty_ns, 73 void (*free)(struct pwm_chip *chip,
47 int period_ns); 74 struct pwm_device *pwm);
48 int (*enable)(struct pwm_chip *chip); 75 int (*config)(struct pwm_chip *chip,
49 void (*disable)(struct pwm_chip *chip); 76 struct pwm_device *pwm,
77 int duty_ns, int period_ns);
78 int (*enable)(struct pwm_chip *chip,
79 struct pwm_device *pwm);
80 void (*disable)(struct pwm_chip *chip,
81 struct pwm_device *pwm);
50 struct module *owner; 82 struct module *owner;
51}; 83};
52 84
53/** 85/**
54 * struct pwm_chip - abstract a PWM 86 * struct pwm_chip - abstract a PWM controller
55 * @pwm_id: global PWM device index 87 * @dev: device providing the PWMs
56 * @label: PWM device label 88 * @list: list node for internal use
57 * @ops: controller operations 89 * @ops: callbacks for this PWM controller
90 * @base: number of first PWM controlled by this chip
91 * @npwm: number of PWMs controlled by this chip
92 * @pwms: array of PWM devices allocated by the framework
58 */ 93 */
59struct pwm_chip { 94struct pwm_chip {
60 int pwm_id; 95 struct device *dev;
61 const char *label; 96 struct list_head list;
62 struct pwm_ops *ops; 97 const struct pwm_ops *ops;
98 int base;
99 unsigned int npwm;
100
101 struct pwm_device *pwms;
63}; 102};
64 103
104int pwm_set_chip_data(struct pwm_device *pwm, void *data);
105void *pwm_get_chip_data(struct pwm_device *pwm);
106
65int pwmchip_add(struct pwm_chip *chip); 107int pwmchip_add(struct pwm_chip *chip);
66int pwmchip_remove(struct pwm_chip *chip); 108int pwmchip_remove(struct pwm_chip *chip);
109struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
110 unsigned int index,
111 const char *label);
67#endif 112#endif
68 113
69#endif /* __LINUX_PWM_H */ 114#endif /* __LINUX_PWM_H */