diff mbox series

[2/3] PWM framework: add support referencing PWMs from ACPI

Message ID 254cc03d5467ba7db0f5677646266768e5b24127.1559127603.git.nikolaus.voss@loewensteinmedical.de (mailing list archive)
State Superseded, archived
Headers show
Series PWM framework: add support referencing PWMs from ACPI | expand

Commit Message

Nikolaus Voss May 29, 2019, 12:18 p.m. UTC
In analogy to referencing a GPIO using the "gpios" property from ACPI,
support referencing a PWM using the "pwms" property.

ACPI entries must look like
 Package () {"pwms", Package ()
     { <PWM device reference>, <PWM index>, <PWM period> [, <PWM flags>]}}

In contrast to the DT implementation, only _one_ PWM entry in the "pwms"
property is supported. As a consequence "pwm-names"-property and
con_id lookup aren't supported.

Support for ACPI is added via the firmware-node framework which is an
abstraction layer on top of ACPI/DT. To keep this patch clean, DT and
ACPI paths are kept separate. The firmware-node framework could be used
to unify both paths in a future patch.

To support leds-pwm driver, an additional method devm_fwnode_pwm_get()
which supports both ACPI and DT configuration is exported.

Signed-off-by: Nikolaus Voss <nikolaus.voss@loewensteinmedical.de>
---
 drivers/pwm/core.c  | 112 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pwm.h |   9 ++++
 2 files changed, 121 insertions(+)

Comments

Dan Murphy May 30, 2019, 2:54 p.m. UTC | #1
Nikolaus

On 5/29/19 7:18 AM, Nikolaus Voss wrote:
> In analogy to referencing a GPIO using the "gpios" property from ACPI,
> support referencing a PWM using the "pwms" property.
>
> ACPI entries must look like
>   Package () {"pwms", Package ()
>       { <PWM device reference>, <PWM index>, <PWM period> [, <PWM flags>]}}
>
> In contrast to the DT implementation, only _one_ PWM entry in the "pwms"
> property is supported. As a consequence "pwm-names"-property and
> con_id lookup aren't supported.
>
> Support for ACPI is added via the firmware-node framework which is an
> abstraction layer on top of ACPI/DT. To keep this patch clean, DT and
> ACPI paths are kept separate. The firmware-node framework could be used
> to unify both paths in a future patch.
>
> To support leds-pwm driver, an additional method devm_fwnode_pwm_get()
> which supports both ACPI and DT configuration is exported.
>
> Signed-off-by: Nikolaus Voss <nikolaus.voss@loewensteinmedical.de>
> ---
>   drivers/pwm/core.c  | 112 ++++++++++++++++++++++++++++++++++++++++++++
>   include/linux/pwm.h |   9 ++++
>   2 files changed, 121 insertions(+)
>
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 275b5f399a1a..1d788c05193e 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -6,6 +6,7 @@
>    * Copyright (C) 2011-2012 Avionic Design GmbH
>    */
>   
> +#include <linux/acpi.h>
>   #include <linux/module.h>
>   #include <linux/pwm.h>
>   #include <linux/radix-tree.h>
> @@ -700,6 +701,75 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
>   }
>   EXPORT_SYMBOL_GPL(of_pwm_get);
>   
> +static struct pwm_chip *device_to_pwmchip(struct device *dev)
> +{
> +	struct pwm_chip *chip;
> +
> +	mutex_lock(&pwm_lock);
> +
> +	list_for_each_entry(chip, &pwm_chips, list) {
> +		struct acpi_device *adev = ACPI_COMPANION(chip->dev);
> +
> +		if ((chip->dev == dev) || (adev && &adev->dev == dev)) {
> +			mutex_unlock(&pwm_lock);
> +			return chip;
> +		}
> +	}
> +
> +	mutex_unlock(&pwm_lock);
> +
> +	return ERR_PTR(-EPROBE_DEFER);
> +}
> +
> +/**
> + * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI
> + * @fwnode: firmware node to get the "pwm" property from
> + *
> + * Returns the PWM device parsed from the fwnode and index specified in the
> + * "pwms" property or a negative error-code on failure.
> + * Values parsed from the device tree are stored in the returned PWM device
> + * object.
> + *
> + * This is analogous to of_pwm_get() except con_id is not yet supported.
> + * ACPI entries must look like
> + * Package () {"pwms", Package ()
> + *     { <PWM device reference>, <PWM index>, <PWM period> [, <PWM flags>]}}
> + *
> + * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
> + * error code on failure.
> + */
> +struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode)
static?
> +{
> +	struct fwnode_reference_args args;
> +	struct pwm_chip *chip;
> +	struct pwm_device *pwm = ERR_PTR(-ENODEV);
> +	int ret;
> +
> +	memset(&args, 0, sizeof(args));

args should be zero'd out when initialized on the stack so this is 
necessary.

> +	ret = __acpi_node_get_property_reference(fwnode, "pwms", 0, 3, &args);
> +
> +	if (!to_acpi_device_node(args.fwnode))
> +		return ERR_PTR(-EINVAL);
Add new line
> +	if (args.nargs < 2)
> +		return ERR_PTR(-EPROTO);
> +
> +	chip = device_to_pwmchip(&to_acpi_device_node(args.fwnode)->dev);
> +	if (IS_ERR(chip))
> +		return ERR_CAST(chip);
> +
> +	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 (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED)
> +		pwm->args.polarity = PWM_POLARITY_INVERSED;
> +
> +	return pwm;
> +}
> +
>   /**
>    * pwm_add_table() - register PWM device consumers
>    * @table: array of consumers to register
> @@ -763,6 +833,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
>   	if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
>   		return of_pwm_get(dev->of_node, con_id);
>   
> +	/* then lookup via ACPI */
> +	if (dev && is_acpi_node(dev->fwnode))
> +		return acpi_pwm_get(dev->fwnode);
> +
>   	/*
>   	 * We look up the provider in the static table typically provided by
>   	 * board setup code. We first try to lookup the consumer device by
> @@ -942,6 +1016,44 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
>   }
>   EXPORT_SYMBOL_GPL(devm_of_pwm_get);
>   
> +/**
> + * devm_fwnode_pwm_get() - request a resource managed PWM from firmware node
> + * @dev: device for PWM consumer
> + * @fwnode: firmware node to get the PWM from
> + * @con_id: consumer name
> + *
> + * Returns the PWM device parsed from the firmware node. See of_pwm_get() and
> + * acpi_pwm_get() for a detailed description.
> + *
> + * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
> + * error code on failure.
> + */
> +struct pwm_device *devm_fwnode_pwm_get(struct device *dev,
> +				       struct fwnode_handle *fwnode,
> +				       const char *con_id)

I am wondering if it would be better just to convert the existing of_ 
calls to device_property calls and make it more generic.

Dan

> +{
> +	struct pwm_device **ptr, *pwm = ERR_PTR(-ENODEV);
> +
> +	ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	if (is_of_node(fwnode))
> +		pwm = of_pwm_get(to_of_node(fwnode), con_id);
> +	else if (is_acpi_node(fwnode))
> +		pwm = acpi_pwm_get(fwnode);
> +
> +	if (!IS_ERR(pwm)) {
> +		*ptr = pwm;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return pwm;
> +}
> +EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get);
> +
>   static int devm_pwm_match(struct device *dev, void *res, void *data)
>   {
>   	struct pwm_device **p = res;
> diff --git a/include/linux/pwm.h b/include/linux/pwm.h
> index eaa5c6e3fc9f..1a635916cdfb 100644
> --- a/include/linux/pwm.h
> +++ b/include/linux/pwm.h
> @@ -411,6 +411,9 @@ void pwm_put(struct pwm_device *pwm);
>   struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id);
>   struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
>   				   const char *con_id);
> +struct pwm_device *devm_fwnode_pwm_get(struct device *dev,
> +				       struct fwnode_handle *fwnode,
> +				       const char *con_id);
>   void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
>   #else
>   static inline struct pwm_device *pwm_request(int pwm_id, const char *label)
> @@ -516,6 +519,12 @@ static inline struct pwm_device *devm_of_pwm_get(struct device *dev,
>   	return ERR_PTR(-ENODEV);
>   }
>   
> +static inline struct pwm_device *devm_fwnode_pwm_get(
> +	struct device *dev, struct fwnode_handle *fwnode, const char *con_id)
> +{
> +	return ERR_PTR(-ENODEV);
> +}
> +
>   static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm)
>   {
>   }
Pavel Machek May 31, 2019, 12:24 p.m. UTC | #2
Hi!

> >+struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode)
> static?
> >+{
> >+	struct fwnode_reference_args args;
> >+	struct pwm_chip *chip;
> >+	struct pwm_device *pwm = ERR_PTR(-ENODEV);
> >+	int ret;
> >+
> >+	memset(&args, 0, sizeof(args));
> 
> args should be zero'd out when initialized on the stack so this is
> necessary.

What?
								Pavel
Nikolaus Voss June 3, 2019, 9:27 a.m. UTC | #3
Dan,

On Thu, 30 May 2019, Dan Murphy wrote:
> Nikolaus
>
> On 5/29/19 7:18 AM, Nikolaus Voss wrote:
>> In analogy to referencing a GPIO using the "gpios" property from ACPI,
>> support referencing a PWM using the "pwms" property.
>>
>> ACPI entries must look like
>>   Package () {"pwms", Package ()
>>       { <PWM device reference>, <PWM index>, <PWM period> [, <PWM flags>]}}
>>
>> In contrast to the DT implementation, only _one_ PWM entry in the "pwms"
>> property is supported. As a consequence "pwm-names"-property and
>> con_id lookup aren't supported.
>>
>> Support for ACPI is added via the firmware-node framework which is an
>> abstraction layer on top of ACPI/DT. To keep this patch clean, DT and
>> ACPI paths are kept separate. The firmware-node framework could be used
>> to unify both paths in a future patch.
>>
>> To support leds-pwm driver, an additional method devm_fwnode_pwm_get()
>> which supports both ACPI and DT configuration is exported.
>>
>> Signed-off-by: Nikolaus Voss <nikolaus.voss@loewensteinmedical.de>
>> ---
>>   drivers/pwm/core.c  | 112 ++++++++++++++++++++++++++++++++++++++++++++
>>   include/linux/pwm.h |   9 ++++
>>   2 files changed, 121 insertions(+)
>>
>> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
>> index 275b5f399a1a..1d788c05193e 100644
>> --- a/drivers/pwm/core.c
>> +++ b/drivers/pwm/core.c
>> @@ -6,6 +6,7 @@
>>    * Copyright (C) 2011-2012 Avionic Design GmbH
>>    */
>>
>> +#include <linux/acpi.h>
>>   #include <linux/module.h>
>>   #include <linux/pwm.h>
>>   #include <linux/radix-tree.h>
>> @@ -700,6 +701,75 @@ struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
>>   }
>>   EXPORT_SYMBOL_GPL(of_pwm_get);
>>
>> +static struct pwm_chip *device_to_pwmchip(struct device *dev)
>> +{
>> +     struct pwm_chip *chip;
>> +
>> +     mutex_lock(&pwm_lock);
>> +
>> +     list_for_each_entry(chip, &pwm_chips, list) {
>> +             struct acpi_device *adev = ACPI_COMPANION(chip->dev);
>> +
>> +             if ((chip->dev == dev) || (adev && &adev->dev == dev)) {
>> +                     mutex_unlock(&pwm_lock);
>> +                     return chip;
>> +             }
>> +     }
>> +
>> +     mutex_unlock(&pwm_lock);
>> +
>> +     return ERR_PTR(-EPROBE_DEFER);
>> +}
>> +
>> +/**
>> + * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI
>> + * @fwnode: firmware node to get the "pwm" property from
>> + *
>> + * Returns the PWM device parsed from the fwnode and index specified in the
>> + * "pwms" property or a negative error-code on failure.
>> + * Values parsed from the device tree are stored in the returned PWM device
>> + * object.
>> + *
>> + * This is analogous to of_pwm_get() except con_id is not yet supported.
>> + * ACPI entries must look like
>> + * Package () {"pwms", Package ()
>> + *     { <PWM device reference>, <PWM index>, <PWM period> [, <PWM flags>]}}
>> + *
>> + * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
>> + * error code on failure.
>> + */
>> +struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode)
> static?
yes, changed.

>> +{
>> +     struct fwnode_reference_args args;
>> +     struct pwm_chip *chip;
>> +     struct pwm_device *pwm = ERR_PTR(-ENODEV);
>> +     int ret;
>> +
>> +     memset(&args, 0, sizeof(args));
>
> args should be zero'd out when initialized on the stack so this is
> necessary.
>
>> +     ret = __acpi_node_get_property_reference(fwnode, "pwms", 0, 3, &args);
>> +
>> +     if (!to_acpi_device_node(args.fwnode))
>> +             return ERR_PTR(-EINVAL);
> Add new line
ok

>> +     if (args.nargs < 2)
>> +             return ERR_PTR(-EPROTO);
>> +
>> +     chip = device_to_pwmchip(&to_acpi_device_node(args.fwnode)->dev);
>> +     if (IS_ERR(chip))
>> +             return ERR_CAST(chip);
>> +
>> +     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 (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED)
>> +             pwm->args.polarity = PWM_POLARITY_INVERSED;
>> +
>> +     return pwm;
>> +}
>> +
>>   /**
>>    * pwm_add_table() - register PWM device consumers
>>    * @table: array of consumers to register
>> @@ -763,6 +833,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
>>       if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
>>               return of_pwm_get(dev->of_node, con_id);
>>
>> +     /* then lookup via ACPI */
>> +     if (dev && is_acpi_node(dev->fwnode))
>> +             return acpi_pwm_get(dev->fwnode);
>> +
>>       /*
>>        * We look up the provider in the static table typically provided by
>>        * board setup code. We first try to lookup the consumer device by
>> @@ -942,6 +1016,44 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
>>   }
>>   EXPORT_SYMBOL_GPL(devm_of_pwm_get);
>>
>> +/**
>> + * devm_fwnode_pwm_get() - request a resource managed PWM from firmware node
>> + * @dev: device for PWM consumer
>> + * @fwnode: firmware node to get the PWM from
>> + * @con_id: consumer name
>> + *
>> + * Returns the PWM device parsed from the firmware node. See of_pwm_get() and
>> + * acpi_pwm_get() for a detailed description.
>> + *
>> + * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
>> + * error code on failure.
>> + */
>> +struct pwm_device *devm_fwnode_pwm_get(struct device *dev,
>> +                                    struct fwnode_handle *fwnode,
>> +                                    const char *con_id)
>
> I am wondering if it would be better just to convert the existing of_
> calls to device_property calls and make it more generic.

With this first patch I wanted to keep DT and ACPI paths separate. Merging 
the two paths as far as possible is reasonable but should be done in a 
second step.

Nikolaus
diff mbox series

Patch

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 275b5f399a1a..1d788c05193e 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -6,6 +6,7 @@ 
  * Copyright (C) 2011-2012 Avionic Design GmbH
  */
 
+#include <linux/acpi.h>
 #include <linux/module.h>
 #include <linux/pwm.h>
 #include <linux/radix-tree.h>
@@ -700,6 +701,75 @@  struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id)
 }
 EXPORT_SYMBOL_GPL(of_pwm_get);
 
+static struct pwm_chip *device_to_pwmchip(struct device *dev)
+{
+	struct pwm_chip *chip;
+
+	mutex_lock(&pwm_lock);
+
+	list_for_each_entry(chip, &pwm_chips, list) {
+		struct acpi_device *adev = ACPI_COMPANION(chip->dev);
+
+		if ((chip->dev == dev) || (adev && &adev->dev == dev)) {
+			mutex_unlock(&pwm_lock);
+			return chip;
+		}
+	}
+
+	mutex_unlock(&pwm_lock);
+
+	return ERR_PTR(-EPROBE_DEFER);
+}
+
+/**
+ * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI
+ * @fwnode: firmware node to get the "pwm" property from
+ *
+ * Returns the PWM device parsed from the fwnode and index specified in the
+ * "pwms" property or a negative error-code on failure.
+ * Values parsed from the device tree are stored in the returned PWM device
+ * object.
+ *
+ * This is analogous to of_pwm_get() except con_id is not yet supported.
+ * ACPI entries must look like
+ * Package () {"pwms", Package ()
+ *     { <PWM device reference>, <PWM index>, <PWM period> [, <PWM flags>]}}
+ *
+ * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode)
+{
+	struct fwnode_reference_args args;
+	struct pwm_chip *chip;
+	struct pwm_device *pwm = ERR_PTR(-ENODEV);
+	int ret;
+
+	memset(&args, 0, sizeof(args));
+	ret = __acpi_node_get_property_reference(fwnode, "pwms", 0, 3, &args);
+
+	if (!to_acpi_device_node(args.fwnode))
+		return ERR_PTR(-EINVAL);
+	if (args.nargs < 2)
+		return ERR_PTR(-EPROTO);
+
+	chip = device_to_pwmchip(&to_acpi_device_node(args.fwnode)->dev);
+	if (IS_ERR(chip))
+		return ERR_CAST(chip);
+
+	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 (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED)
+		pwm->args.polarity = PWM_POLARITY_INVERSED;
+
+	return pwm;
+}
+
 /**
  * pwm_add_table() - register PWM device consumers
  * @table: array of consumers to register
@@ -763,6 +833,10 @@  struct pwm_device *pwm_get(struct device *dev, const char *con_id)
 	if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node)
 		return of_pwm_get(dev->of_node, con_id);
 
+	/* then lookup via ACPI */
+	if (dev && is_acpi_node(dev->fwnode))
+		return acpi_pwm_get(dev->fwnode);
+
 	/*
 	 * We look up the provider in the static table typically provided by
 	 * board setup code. We first try to lookup the consumer device by
@@ -942,6 +1016,44 @@  struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(devm_of_pwm_get);
 
+/**
+ * devm_fwnode_pwm_get() - request a resource managed PWM from firmware node
+ * @dev: device for PWM consumer
+ * @fwnode: firmware node to get the PWM from
+ * @con_id: consumer name
+ *
+ * Returns the PWM device parsed from the firmware node. See of_pwm_get() and
+ * acpi_pwm_get() for a detailed description.
+ *
+ * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded
+ * error code on failure.
+ */
+struct pwm_device *devm_fwnode_pwm_get(struct device *dev,
+				       struct fwnode_handle *fwnode,
+				       const char *con_id)
+{
+	struct pwm_device **ptr, *pwm = ERR_PTR(-ENODEV);
+
+	ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return ERR_PTR(-ENOMEM);
+
+	if (is_of_node(fwnode))
+		pwm = of_pwm_get(to_of_node(fwnode), con_id);
+	else if (is_acpi_node(fwnode))
+		pwm = acpi_pwm_get(fwnode);
+
+	if (!IS_ERR(pwm)) {
+		*ptr = pwm;
+		devres_add(dev, ptr);
+	} else {
+		devres_free(ptr);
+	}
+
+	return pwm;
+}
+EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get);
+
 static int devm_pwm_match(struct device *dev, void *res, void *data)
 {
 	struct pwm_device **p = res;
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index eaa5c6e3fc9f..1a635916cdfb 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -411,6 +411,9 @@  void pwm_put(struct pwm_device *pwm);
 struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id);
 struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np,
 				   const char *con_id);
+struct pwm_device *devm_fwnode_pwm_get(struct device *dev,
+				       struct fwnode_handle *fwnode,
+				       const char *con_id);
 void devm_pwm_put(struct device *dev, struct pwm_device *pwm);
 #else
 static inline struct pwm_device *pwm_request(int pwm_id, const char *label)
@@ -516,6 +519,12 @@  static inline struct pwm_device *devm_of_pwm_get(struct device *dev,
 	return ERR_PTR(-ENODEV);
 }
 
+static inline struct pwm_device *devm_fwnode_pwm_get(
+	struct device *dev, struct fwnode_handle *fwnode, const char *con_id)
+{
+	return ERR_PTR(-ENODEV);
+}
+
 static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm)
 {
 }