blob: 0b8a38eca23ca36fc25f26e80fbec2d89e9ea0db [file] [log] [blame]
Sascha Hauer0c2498f2011-01-28 09:40:40 +01001/*
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);