// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic pwmlib implementation
*
* Copyright (C) 2011 Sascha Hauer <s.hauer@pengutronix.de>
* Copyright (C) 2011-2012 Avionic Design GmbH
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/of.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>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <dt-bindings/pwm/pwm.h>
#define CREATE_TRACE_POINTS
#include <trace/events/pwm.h>
#define MAX_PWMS 1024
static DEFINE_MUTEX(pwm_lookup_lock);
static LIST_HEAD(pwm_lookup_list);
/* protects access to pwm_chips and allocated_pwms */
static DEFINE_MUTEX(pwm_lock);
static LIST_HEAD(pwm_chips);
static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
/* Called with pwm_lock held */
static int alloc_pwms(unsigned int count)
{
unsigned int start;
start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, 0,
count, 0);
if (start + count > MAX_PWMS)
return -ENOSPC;
bitmap_set(allocated_pwms, start, count);
return start;
}
/* Called with pwm_lock held */
static void free_pwms(struct pwm_chip *chip)
{
bitmap_clear(allocated_pwms, chip->base, chip->npwm);
kfree(chip->pwms);
chip->pwms = NULL;
}
static struct pwm_chip *pwmchip_find_by_name(const char *name)
{
struct pwm_chip *chip;
if (!name)
return NULL;
mutex_lock(&pwm_lock);
list_for_each_entry(chip, &pwm_chips, list) {
const char *chip_name = dev_name(chip->dev);
if (chip_name && strcmp(chip_name, name) == 0) {
mutex_unlock(&pwm_lock);
return chip;
}
}
mutex_unlock(&pwm_lock);
return NULL;
}
static int pwm_device_request(struct pwm_device *pwm, const char *label)
{
int err;
if (test_bit(PWMF_REQUESTED, &pwm->flags))
return -EBUSY;
if (!try_module_get(pwm->chip->owner))
return -ENODEV;
if (pwm->chip->ops->request) {
err = pwm->chip->ops->request(pwm->chip, pwm);
if (err) {
module_put(pwm->chip->owner);
return err;
}
}
if (pwm->chip->ops->get_state) {
/*
* Zero-initialize state because most drivers are unaware of
* .usage_power. The other members of state are supposed to be
* set by lowlevel drivers. We still initialize the whole
* structure for simplicity even though this might paper over
* faulty implementations of .get_state().
*/
struct pwm_state state = { 0, };
err = pwm->chip->ops->get_state(pwm->chip, pwm, &state);
trace_pwm_get(pwm, &state, err);
if (!err)
pwm->state = state;
if (IS_ENABLED(CONFIG_PWM_DEBUG))
pwm->last = pwm->state;
}
set_bit(PWMF_REQUESTED, &pwm->flags);
pwm->label = label;
return 0;
}
struct pwm_device *
of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args)
{
struct pwm_device *pwm;
if (chip->of_pwm_n_cells < 2)
return ERR_PTR(-EINVAL);
/* flags in the third cell are optional */
if (args->args_count < 2)
return ERR_PTR(-EINVAL);
if (args->args[0] >= chip->npwm)
return ERR_PTR(-EINVAL);
pwm = pwm_request_from_chip(chip, args->args[0], NULL);
if (IS_ERR(pwm))
return pwm;
pwm->args.period = args->args[1];
pwm->args.polarity = PWM_POLARITY_NORMAL;
if (chip->of_pwm_n_cells >= 3) {
if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
pwm->args.polarity = PWM_POLARITY_INVERSED;
}
return pwm;
}
EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags);