// 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/idr.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>
static DEFINE_MUTEX(pwm_lookup_lock);
static LIST_HEAD(pwm_lookup_list);
/* protects access to pwm_chips */
static DEFINE_MUTEX(pwm_lock);
static DEFINE_IDR(pwm_chips);
static struct pwm_chip *pwmchip_find_by_name(const char *name)
{
struct pwm_chip *chip;
unsigned long id, tmp;
if (!name)
return NULL;
mutex_lock(&pwm_lock);
idr_for_each_entry_ul(&pwm_chips, chip, tmp, id) {
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;
struct pwm_chip *chip = pwm->chip;
const struct pwm_ops *ops = chip->ops;
if (test_bit(PWMF_REQUESTED, &pwm->flags))
return -EBUSY;
if (!try_module_get(chip->owner))
return -ENODEV;
if (ops->request) {
err = ops->request(chip, pwm);
if (err) {
module_put(chip->owner);
return err;
}
}
if (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 = ops->get_state(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);
struct pwm_device *
of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
{
struct pwm_device *pwm;
if (chip->of_pwm_n_cells < 1)
return ERR_PTR(-EINVAL);
/* validate that one cell is specified, optionally with flags */
if (args->args_count != 1 && args->args_count != 2)
return ERR_PTR(-EINVAL);
pwm = pwm_request_from_chip(chip, 0, NULL);
if (IS_ERR(pwm))
return pwm;
pwm->args.period = args->args[0];
pwm->args.polarity = PWM_POLARITY_NORMAL;
if (args->args_count