pwm: Add device tree support
This patch adds helpers to support device tree bindings for the generic PWM API. Device tree binding documentation for PWM controllers is also provided. Acked-by: Arnd Bergmann <arnd@arndb.de> Reviewed-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
This commit is contained in:
@@ -129,6 +129,45 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
|
||||
const struct of_phandle_args *args)
|
||||
{
|
||||
struct pwm_device *pwm;
|
||||
|
||||
if (pc->of_pwm_n_cells < 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (args->args[0] >= pc->npwm)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pwm = pwm_request_from_chip(pc, args->args[0], NULL);
|
||||
if (IS_ERR(pwm))
|
||||
return pwm;
|
||||
|
||||
pwm_set_period(pwm, args->args[1]);
|
||||
|
||||
return pwm;
|
||||
}
|
||||
|
||||
void of_pwmchip_add(struct pwm_chip *chip)
|
||||
{
|
||||
if (!chip->dev || !chip->dev->of_node)
|
||||
return;
|
||||
|
||||
if (!chip->of_xlate) {
|
||||
chip->of_xlate = of_pwm_simple_xlate;
|
||||
chip->of_pwm_n_cells = 2;
|
||||
}
|
||||
|
||||
of_node_get(chip->dev->of_node);
|
||||
}
|
||||
|
||||
void of_pwmchip_remove(struct pwm_chip *chip)
|
||||
{
|
||||
if (chip->dev && chip->dev->of_node)
|
||||
of_node_put(chip->dev->of_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_set_chip_data() - set private chip data for a PWM
|
||||
* @pwm: PWM device
|
||||
@@ -201,6 +240,9 @@ int pwmchip_add(struct pwm_chip *chip)
|
||||
|
||||
ret = 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF))
|
||||
of_pwmchip_add(chip);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pwm_lock);
|
||||
return ret;
|
||||
@@ -231,6 +273,10 @@ int pwmchip_remove(struct pwm_chip *chip)
|
||||
}
|
||||
|
||||
list_del_init(&chip->list);
|
||||
|
||||
if (IS_ENABLED(CONFIG_OF))
|
||||
of_pwmchip_remove(chip);
|
||||
|
||||
free_pwms(chip);
|
||||
|
||||
out:
|
||||
@@ -356,6 +402,99 @@ void pwm_disable(struct pwm_device *pwm)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pwm_disable);
|
||||
|
||||
static struct pwm_chip *of_node_to_pwmchip(struct device_node *np)
|
||||
{
|
||||
struct pwm_chip *chip;
|
||||
|
||||
mutex_lock(&pwm_lock);
|
||||
|
||||
list_for_each_entry(chip, &pwm_chips, list)
|
||||
if (chip->dev && chip->dev->of_node == np) {
|
||||
mutex_unlock(&pwm_lock);
|
||||
return chip;
|
||||
}
|
||||
|
||||
mutex_unlock(&pwm_lock);
|
||||
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_pwm_request() - request a PWM via the PWM framework
|
||||
* @np: device node to get the PWM from
|
||||
* @con_id: consumer name
|
||||
*
|
||||
* Returns the PWM device parsed from the phandle and index specified in the
|
||||
* "pwms" property of a device tree node or a negative error-code on failure.
|
||||
* Values parsed from the device tree are stored in the returned PWM device
|
||||
* object.
|
||||
*
|
||||
* If con_id is NULL, the first PWM device listed in the "pwms" property will
|
||||
* be requested. Otherwise the "pwm-names" property is used to do a reverse
|
||||
* lookup of the PWM index. This also means that the "pwm-names" property
|
||||
* becomes mandatory for devices that look up the PWM device via the con_id
|
||||
* parameter.
|
||||
*/
|
||||
static struct pwm_device *of_pwm_request(struct device_node *np,
|
||||
const char *con_id)
|
||||
{
|
||||
struct pwm_device *pwm = NULL;
|
||||
struct of_phandle_args args;
|
||||
struct pwm_chip *pc;
|
||||
int index = 0;
|
||||
int err;
|
||||
|
||||
if (con_id) {
|
||||
index = of_property_match_string(np, "pwm-names", con_id);
|
||||
if (index < 0)
|
||||
return ERR_PTR(index);
|
||||
}
|
||||
|
||||
err = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", index,
|
||||
&args);
|
||||
if (err) {
|
||||
pr_debug("%s(): can't parse \"pwms\" property\n", __func__);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
pc = of_node_to_pwmchip(args.np);
|
||||
if (IS_ERR(pc)) {
|
||||
pr_debug("%s(): PWM chip not found\n", __func__);
|
||||
pwm = ERR_CAST(pc);
|
||||
goto put;
|
||||
}
|
||||
|
||||
if (args.args_count != pc->of_pwm_n_cells) {
|
||||
pr_debug("%s: wrong #pwm-cells for %s\n", np->full_name,
|
||||
args.np->full_name);
|
||||
pwm = ERR_PTR(-EINVAL);
|
||||
goto put;
|
||||
}
|
||||
|
||||
pwm = pc->of_xlate(pc, &args);
|
||||
if (IS_ERR(pwm))
|
||||
goto put;
|
||||
|
||||
/*
|
||||
* If a consumer name was not given, try to look it up from the
|
||||
* "pwm-names" property if it exists. Otherwise use the name of
|
||||
* the user device node.
|
||||
*/
|
||||
if (!con_id) {
|
||||
err = of_property_read_string_index(np, "pwm-names", index,
|
||||
&con_id);
|
||||
if (err < 0)
|
||||
con_id = np->name;
|
||||
}
|
||||
|
||||
pwm->label = con_id;
|
||||
|
||||
put:
|
||||
of_node_put(args.np);
|
||||
|
||||
return pwm;
|
||||
}
|
||||
|
||||
/**
|
||||
* pwm_add_table() - register PWM device consumers
|
||||
* @table: array of consumers to register
|
||||
@@ -378,8 +517,9 @@ void __init pwm_add_table(struct pwm_lookup *table, size_t num)
|
||||
* @dev: device for PWM consumer
|
||||
* @con_id: consumer name
|
||||
*
|
||||
* Look up a PWM chip and a relative index via a table supplied by board setup
|
||||
* code (see pwm_add_table()).
|
||||
* Lookup is first attempted using DT. If the device was not instantiated from
|
||||
* a device tree, a PWM chip and a relative index is looked up via a table
|
||||
* supplied by board setup code (see pwm_add_table()).
|
||||
*
|
||||
* Once a PWM chip has been found the specified PWM device will be requested
|
||||
* and is ready to be used.
|
||||
@@ -394,6 +534,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
|
||||
unsigned int index;
|
||||
unsigned int match;
|
||||
|
||||
/* look up via DT first */
|
||||
if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
|
||||
return of_pwm_request(dev->of_node, con_id);
|
||||
|
||||
/*
|
||||
* We look up the provider in the static table typically provided by
|
||||
* board setup code. We first try to lookup the consumer device by
|
||||
|
Reference in New Issue
Block a user