diff options
Diffstat (limited to 'drivers/pwm/core.c')
| -rw-r--r-- | drivers/pwm/core.c | 227 | 
1 files changed, 227 insertions, 0 deletions
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 @@ +/* + * Generic pwmlib implementation + * + * Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2, or (at your option) + *  any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; see the file COPYING.  If not, write to + *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/pwm.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/device.h> + +struct pwm_device { +	struct			pwm_chip *chip; +	const char		*label; +	unsigned long		flags; +#define FLAG_REQUESTED	0 +#define FLAG_ENABLED	1 +	struct list_head	node; +}; + +static LIST_HEAD(pwm_list); + +static DEFINE_MUTEX(pwm_lock); + +static struct pwm_device *_find_pwm(int pwm_id) +{ +	struct pwm_device *pwm; + +	list_for_each_entry(pwm, &pwm_list, node) { +		if (pwm->chip->pwm_id == pwm_id) +			return pwm; +	} + +	return NULL; +} + +/** + * pwmchip_add() - register a new PWM chip + * @chip: the PWM chip to add + */ +int pwmchip_add(struct pwm_chip *chip) +{ +	struct pwm_device *pwm; +	int ret = 0; + +	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL); +	if (!pwm) +		return -ENOMEM; + +	pwm->chip = chip; + +	mutex_lock(&pwm_lock); + +	if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) { +		ret = -EBUSY; +		goto out; +	} + +	list_add_tail(&pwm->node, &pwm_list); +out: +	mutex_unlock(&pwm_lock); + +	if (ret) +		kfree(pwm); + +	return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_add); + +/** + * pwmchip_remove() - remove a PWM chip + * @chip: the PWM chip to remove + * + * Removes a PWM chip. This function may return busy if the PWM chip provides + * a PWM device that is still requested. + */ +int pwmchip_remove(struct pwm_chip *chip) +{ +	struct pwm_device *pwm; +	int ret = 0; + +	mutex_lock(&pwm_lock); + +	pwm = _find_pwm(chip->pwm_id); +	if (!pwm) { +		ret = -ENOENT; +		goto out; +	} + +	if (test_bit(FLAG_REQUESTED, &pwm->flags)) { +		ret = -EBUSY; +		goto out; +	} + +	list_del(&pwm->node); + +	kfree(pwm); +out: +	mutex_unlock(&pwm_lock); + +	return ret; +} +EXPORT_SYMBOL_GPL(pwmchip_remove); + +/** + * pwm_request() - request a PWM device + * @pwm_id: global PWM device index + * @label: PWM device label + */ +struct pwm_device *pwm_request(int pwm_id, const char *label) +{ +	struct pwm_device *pwm; +	int ret; + +	mutex_lock(&pwm_lock); + +	pwm = _find_pwm(pwm_id); +	if (!pwm) { +		pwm = ERR_PTR(-ENOENT); +		goto out; +	} + +	if (test_bit(FLAG_REQUESTED, &pwm->flags)) { +		pwm = ERR_PTR(-EBUSY); +		goto out; +	} + +	if (!try_module_get(pwm->chip->ops->owner)) { +		pwm = ERR_PTR(-ENODEV); +		goto out; +	} + +	if (pwm->chip->ops->request) { +		ret = pwm->chip->ops->request(pwm->chip); +		if (ret) { +			pwm = ERR_PTR(ret); +			goto out_put; +		} +	} + +	pwm->label = label; +	set_bit(FLAG_REQUESTED, &pwm->flags); + +	goto out; + +out_put: +	module_put(pwm->chip->ops->owner); +out: +	mutex_unlock(&pwm_lock); + +	return pwm; +} +EXPORT_SYMBOL_GPL(pwm_request); + +/** + * pwm_free() - free a PWM device + * @pwm: PWM device + */ +void pwm_free(struct pwm_device *pwm) +{ +	mutex_lock(&pwm_lock); + +	if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) { +		pr_warning("PWM device already freed\n"); +		goto out; +	} + +	pwm->label = NULL; + +	module_put(pwm->chip->ops->owner); +out: +	mutex_unlock(&pwm_lock); +} +EXPORT_SYMBOL_GPL(pwm_free); + +/** + * pwm_config() - change a PWM device configuration + * @pwm: PWM device + * @duty_ns: "on" time (in nanoseconds) + * @period_ns: duration (in nanoseconds) of one cycle + */ +int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) +{ +	return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns); +} +EXPORT_SYMBOL_GPL(pwm_config); + +/** + * pwm_enable() - start a PWM output toggling + * @pwm: PWM device + */ +int pwm_enable(struct pwm_device *pwm) +{ +	if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags)) +		return pwm->chip->ops->enable(pwm->chip); + +	return 0; +} +EXPORT_SYMBOL_GPL(pwm_enable); + +/** + * pwm_disable() - stop a PWM output toggling + * @pwm: PWM device + */ +void pwm_disable(struct pwm_device *pwm) +{ +	if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags)) +		pwm->chip->ops->disable(pwm->chip); +} +EXPORT_SYMBOL_GPL(pwm_disable);  |