All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ryan Mallon <rmallon-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: Thierry Reding
	<thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
	Mitch Bradley <wmb-D5eQfiDGL7eakBO8gow8eQ@public.gmane.org>,
	Mark Brown
	<broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>,
	Mike Frysinger <vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org>,
	Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>,
	Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>,
	Colin Cross <ccross-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org>,
	Rob Herring <rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org>,
	Grant Likely
	<grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>,
	Olof Johansson <olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org>,
	Richard Purdie <rpurdie-Fm38FmjxZ/leoWH0uzbU5w@public.gmane.org>,
	Matthias Kaehlcke
	<matthias-RprLehDfhQ3k1uMJSBkQmQ@public.gmane.org>,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Eric Miao <eric.y.miao-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	Kurt Van Dijck <kurt.van.dijck-/BeEPy95v10@public.gmane.org>
Subject: Re: [PATCH v2 02/10] pwm: Allow chips to support multiple PWMs.
Date: Wed, 08 Feb 2012 09:53:23 +1100	[thread overview]
Message-ID: <4F31AB63.3020301@gmail.com> (raw)
In-Reply-To: <1328541585-24642-3-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>

On 07/02/12 02:19, Thierry Reding wrote:

Hi Thierry,

A few comments below,

~Ryan

> This commit modifies the PWM core to support multiple PWMs per struct
> pwm_chip. It achieves this in a similar way to how gpiolib works, by
> allowing PWM ranges to be requested dynamically (pwm_chip.base == -1) or
> starting at a given offset (pwm_chip.base >= 0). A chip specifies how
> many PWMs it controls using the npwm member. Each of the functions in
> the pwm_ops structure gets an additional argument that specified the PWM
> number (it can be converted to a per-chip index by subtracting the
> chip's base).
> 
> The total maximum number of PWM devices is currently fixed to 64, but
> can easily be made configurable via Kconfig.

It would be better to make the code handle arbitrary numbers of PWMs. A
Kconfig knob becomes annoying when you have more than one platform
configured into the kernel.

> The patch is incomplete in that it doesn't convert any existing drivers
> that are now broken.


Does this patch actually break the drivers in terms of not building or
running? If so, can this patch series be reworked a bit to allow the old
PWM framework to be used until all of the drivers are converted?

> 
> Signed-off-by: Thierry Reding <thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
> ---
> Changes in v2:
>   - add 'struct device *dev' field to pwm_chip, drop label field
>   - use radix tree for PWM descriptors
>   - add pwm_set_chip_data() and pwm_get_chip_data() functions to allow
>     storing and retrieving chip-specific per-PWM data
> 
> TODO:
>   - pass chip-indexed PWM number (PWM descriptor?) to drivers
>   - merge with Sascha's patch
> 
>  drivers/pwm/core.c  |  287 ++++++++++++++++++++++++++++++++++++++-------------
>  include/linux/pwm.h |   37 +++++--
>  2 files changed, 242 insertions(+), 82 deletions(-)
> 
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 71de479..a223bd6 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -17,154 +17,292 @@
>   *  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/of_pwm.h>
>  #include <linux/pwm.h>
> +#include <linux/radix-tree.h>
>  #include <linux/list.h>
>  #include <linux/mutex.h>
>  #include <linux/err.h>
>  #include <linux/slab.h>
>  #include <linux/device.h>
>  
> +#define MAX_PWMS 1024
> +
>  struct pwm_device {
> -	struct			pwm_chip *chip;
> -	const char		*label;
> +	const char *label;
> +	unsigned int pwm;
> +};
> +
> +struct pwm_desc {
> +	struct pwm_chip		*chip;
> +	void			*chip_data;
>  	unsigned long		flags;
>  #define FLAG_REQUESTED	0
>  #define FLAG_ENABLED	1
> -	struct list_head	node;
> +	struct pwm_device	pwm;
>  };


Do pwm_desc and pwm_device really need to be separate structs?
pwm_device only has two fields, which could easily be added to pwm_desc
(and rename it to pmw_device if you like). They are both opaque
structures, so it makes no difference to the users of the framework.

 
> -static LIST_HEAD(pwm_list);
> -
>  static DEFINE_MUTEX(pwm_lock);
> +static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
> +static RADIX_TREE(pwm_desc_tree, GFP_KERNEL);

I missed any discussion of this from v1, but why was this approach
chosen? The bitmap arbitrarily limits the maximum number of PWMs and is
also a little bit (nitpicky) wasteful when a platform doesn't need
anywhere near 1024 PWMs.

In most cases I would expect that platforms only have a handful of PWMs
(say less than 32), in which case a list lookup isn't too bad.

As someone else mentioned, it might be best to drop the global numbering
scheme and have each pwm_chip have its own numbering for its PWMs. So
requesting a PWM would first require getting a handle to the PWM chip it
is on. If a chip has a fixed number of PWMs (e.g. set a registration
time) then the PWMs within a chip can just be an array with O(1) lookup.

> +static struct pwm_desc *pwm_to_desc(unsigned int pwm)
> +{
> +	return radix_tree_lookup(&pwm_desc_tree, pwm);
> +}
> +
> +static struct pwm_desc *alloc_desc(unsigned int pwm)
> +{
> +	struct pwm_desc *desc;
> +
> +	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
> +	if (!desc)
> +		return NULL;
>  
> -static struct pwm_device *_find_pwm(int pwm_id)
> +	return desc;
> +}
> +
> +static void free_desc(unsigned int pwm)
> +{
> +	struct pwm_desc *desc = pwm_to_desc(pwm);
> +
> +	radix_tree_delete(&pwm_desc_tree, pwm);
> +	kfree(desc);
> +}
> +
> +static int alloc_descs(int pwm, unsigned int from, unsigned int count)

>  {

You don't really need the from argument. There is only one caller for
alloc_descs and it passes 0 for from. So you could re-write this as:

	static int alloc_descs(int pwm, unsigned int count)
	{
		unsigned int from = 0;
		...

		if (pwm > MAX_PWMS)
			return -EINVAL;
		if (pwm >= 0)
			from = pwm;

> -	struct pwm_device *pwm;
> +	unsigned int start;
> +	unsigned int i;
> +	int ret = 0;
> +
> +	if (pwm >= 0) {
> +		if (from > pwm)
> +			return -EINVAL;
> +
> +		from = pwm;
> +	}


This doesn't catch some caller errors:

	if (pwm > MAX_PWMS || from > MAX_PWMS)
		return -EINVAL;

> +
> +	start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from,
> +			count, 0);

Do the PWM indexes need to be contiguous for a given PWM chip? You could
probably grab each bit individually. That said, I think a better
solution is to have per-chip numbering.

> -	list_for_each_entry(pwm, &pwm_list, node) {
> -		if (pwm->chip->pwm_id == pwm_id)
> -			return pwm;
> +	if ((pwm >= 0) && (start != pwm))


Don't need the inner parens.

> +		return -EEXIST;
> +
> +	if (start + count > MAX_PWMS)
> +		return -ENOSPC;


Is this possible? Can bitmap_find_next_zero_area return a start position
where the count would exceed the maximum?

> +
> +	for (i = start; i < start + count; i++) {
> +		struct pwm_desc *desc = alloc_desc(i);
> +		if (!desc) {
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +
> +		radix_tree_insert(&pwm_desc_tree, i, desc);
>  	}
>  
> -	return NULL;
> +	bitmap_set(allocated_pwms, start, count);
> +
> +	return start;
> +
> +err:
> +	for (i--; i >= 0; i--)


Nitpick:

	while (--i >= 0)

is a bit simpler.

> +		free_desc(i);
> +
> +	return ret;
> +}
> +
> +static void free_descs(unsigned int from, unsigned int count)
> +{
> +	unsigned int i;
> +
> +	for (i = from; i < from + count; i++)
> +		free_desc(i);
> +
> +	bitmap_clear(allocated_pwms, from, count);
>  }
>  
>  /**
> - * pwmchip_add() - register a new pwm
> - * @chip: the pwm
> - *
> - * register a new pwm. pwm->pwm_id must be initialized. if pwm_id < 0 then
> - * a dynamically assigned id will be used, otherwise the id specified,
> + * pwm_set_chip_data - set private chip data for a PWM
> + * @pwm: PWM number
> + * @data: pointer to chip-specific data
>   */
> -int pwmchip_add(struct pwm_chip *chip)
> +int pwm_set_chip_data(unsigned int pwm, void *data)


This interface is a bit ugly. Shouldn't the user need to have a handle
to the pwm_device in order to access the chip_data? Why should callers
be able to set/grab chip data from random PWMs?

>  {
> -	struct pwm_device *pwm;
> -	int ret = 0;
> +	struct pwm_desc *desc = pwm_to_desc(pwm);
>  
> -	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
> -	if (!pwm)
> -		return -ENOMEM;
> +	if (!desc)
> +		return -EINVAL;
>  
> -	pwm->chip = chip;
> +	desc->chip_data = data;
> +	return 0;
> +}
> +
> +/**
> + * pwm_get_chip_data - get private chip data for a PWM
> + * @pwm: PWM number
> + */
> +void *pwm_get_chip_data(unsigned int pwm)
> +{

> +	struct pwm_desc *desc = pwm_to_desc(pwm);
> +
> +	return desc ? desc->chip_data : NULL;
> +}
> +
> +/**
> + * pwmchip_add() - register a new PWM chip
> + * @chip: the PWM chip to add
> + *
> + * Register a new PWM chip. If pwm->base < 0 then a dynamically assigned base
> + * will be used.
> + */
> +int pwmchip_add(struct pwm_chip *chip)
> +{
> +	unsigned int i;
> +	int ret;
>  
>  	mutex_lock(&pwm_lock);
>  
> -	if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) {
> -		ret = -EBUSY;
> +	ret = alloc_descs(chip->base, 0, chip->npwm);
> +	if (ret < 0)
>  		goto out;
> +
> +	chip->base = ret;
> +	ret = 0;
> +
> +	/* initialize descriptors */
> +	for (i = chip->base; i < chip->base + chip->npwm; i++) {
> +		struct pwm_desc *desc = pwm_to_desc(i);
> +
> +		if (!desc) {
> +			pr_debug("pwm: descriptor %u not initialized\n", i);


Should this be a WARN_ON?

> +			continue;
> +		}
> +
> +		desc->chip = chip;
>  	}
>  
> -	list_add_tail(&pwm->node, &pwm_list);
> +	of_pwmchip_add(chip);
> +
>  out:
>  	mutex_unlock(&pwm_lock);
> -
> -	if (ret)
> -		kfree(pwm);
> -
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(pwmchip_add);
>  
>  /**
> - * pwmchip_remove() - remove a pwm
> - * @chip: the pwm
> + * pwmchip_remove() - remove a PWM chip
> + * @chip: the PWM chip to remove
>   *
> - * remove a pwm. This function may return busy if the pwm is still requested.
> + * 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;
> +	unsigned int i;
>  	int ret = 0;
>  
>  	mutex_lock(&pwm_lock);
>  
> -	pwm = _find_pwm(chip->pwm_id);
> -	if (!pwm) {
> -		ret = -ENOENT;
> -		goto out;
> -	}
> +	for (i = chip->base; i < chip->base + chip->npwm; i++) {
> +		struct pwm_desc *desc = pwm_to_desc(i);
>  
> -	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
> -		ret = -EBUSY;
> -		goto out;
> +		if (test_bit(FLAG_REQUESTED, &desc->flags)) {
> +			ret = -EBUSY;
> +			goto out;
> +		}
>  	}
>  
> -	list_del(&pwm->node);
> +	free_descs(chip->base, chip->npwm);
> +	of_pwmchip_remove(chip);
>  
> -	kfree(pwm);
>  out:
>  	mutex_unlock(&pwm_lock);
> -
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(pwmchip_remove);
>  
> +/**
> + * pwmchip_find() - iterator for locating a specific pwm_chip
> + * @data: data to pass to match function
> + * @match: callback function to check pwm_chip
> + */
> +struct pwm_chip *pwmchip_find(void *data, int (*match)(struct pwm_chip *chip,
> +						       void *data))
> +{
> +	struct pwm_chip *chip = NULL;
> +	unsigned long i;
> +
> +	mutex_lock(&pwm_lock);
> +
> +	for_each_set_bit(i, allocated_pwms, MAX_PWMS) {
> +		struct pwm_desc *desc = pwm_to_desc(i);
> +
> +		if (!desc || !desc->chip)
> +			continue;
> +
> +		if (match(desc->chip, data)) {
> +			chip = desc->chip;
> +			break;
> +		}
> +	}
> +
> +	mutex_unlock(&pwm_lock);
> +
> +	return chip;
> +}
> +EXPORT_SYMBOL_GPL(pwmchip_find);


So, propreitary modules are not allowed to use PWMs?

> +
>  /*
>   * pwm_request - request a PWM device
>   */
> -struct pwm_device *pwm_request(int pwm_id, const char *label)
> +struct pwm_device *pwm_request(int pwm, const char *label)
>  {
> -	struct pwm_device *pwm;
> +	struct pwm_device *dev;
> +	struct pwm_desc *desc;
>  	int ret;
>  
> +	if ((pwm < 0) || (pwm >= MAX_PWMS))


Don't need the inner parens.

> +		return ERR_PTR(-ENOENT);


-EINVAL maybe?

> +
>  	mutex_lock(&pwm_lock);
>  
> -	pwm = _find_pwm(pwm_id);
> -	if (!pwm) {
> -		pwm = ERR_PTR(-ENOENT);
> -		goto out;
> -	}
> +	desc = pwm_to_desc(pwm);
> +	dev = &desc->pwm;


This looks wrong. If the pwm_desc being requested doesn't exist, won't
this oops?

>  
> -	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
> -		pwm = ERR_PTR(-EBUSY);
> +	if (test_bit(FLAG_REQUESTED, &desc->flags)) {
> +		dev = ERR_PTR(-EBUSY);
>  		goto out;
>  	}
>  
> -	if (!try_module_get(pwm->chip->ops->owner)) {
> -		pwm = ERR_PTR(-ENODEV);
> +	if (!try_module_get(desc->chip->ops->owner)) {
> +		dev = ERR_PTR(-ENODEV);
>  		goto out;
>  	}
>  
> -	if (pwm->chip->ops->request) {
> -		ret = pwm->chip->ops->request(pwm->chip);
> +	if (desc->chip->ops->request) {
> +		ret = desc->chip->ops->request(desc->chip, pwm);
>  		if (ret) {
> -			pwm = ERR_PTR(ret);
> +			dev = ERR_PTR(ret);
>  			goto out_put;
>  		}
>  	}
>  
> -	pwm->label = label;
> -	set_bit(FLAG_REQUESTED, &pwm->flags);
> +	dev->label = label;
> +	dev->pwm = pwm;
> +	set_bit(FLAG_REQUESTED, &desc->flags);
>  
>  	goto out;
>  
>  out_put:
> -	module_put(pwm->chip->ops->owner);
> +	module_put(desc->chip->ops->owner);
>  out:
>  	mutex_unlock(&pwm_lock);
>  
> -	return pwm;
> +	return dev;
>  }
>  EXPORT_SYMBOL_GPL(pwm_request);
>  
> @@ -173,16 +311,19 @@ EXPORT_SYMBOL_GPL(pwm_request);
>   */
>  void pwm_free(struct pwm_device *pwm)
>  {
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +
>  	mutex_lock(&pwm_lock);

pwm_lock is used to protect global addition and removal from the
radix/tree bitmap. Here you are also using it to protect the fields of a
specific pwm device? Maybe it would be better to have a per pwm device
lock for this?

> -	if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) {
> +	if (!test_and_clear_bit(FLAG_REQUESTED, &desc->flags)) {
>  		pr_warning("PWM device already freed\n");
>  		goto out;
>  	}
>  
>  	pwm->label = NULL;
> +	pwm->pwm = 0;


Why do this? Isn't index 0 a valid PWM index?a

>  
> -	module_put(pwm->chip->ops->owner);
> +	module_put(desc->chip->ops->owner);
>  out:
>  	mutex_unlock(&pwm_lock);
>  }
> @@ -193,7 +334,9 @@ EXPORT_SYMBOL_GPL(pwm_free);
>   */
>  int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>  {
> -	return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns);
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +	return desc->chip->ops->config(desc->chip, pwm->pwm, duty_ns,
> +			period_ns);
>  }
>  EXPORT_SYMBOL_GPL(pwm_config);
>  
> @@ -202,8 +345,10 @@ EXPORT_SYMBOL_GPL(pwm_config);
>   */
>  int pwm_enable(struct pwm_device *pwm)
>  {
> -	if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags))
> -		return pwm->chip->ops->enable(pwm->chip);
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +
> +	if (!test_and_set_bit(FLAG_ENABLED, &desc->flags))
> +		return desc->chip->ops->enable(desc->chip, pwm->pwm);
>  
>  	return 0;
>  }
> @@ -214,7 +359,9 @@ EXPORT_SYMBOL_GPL(pwm_enable);
>   */
>  void pwm_disable(struct pwm_device *pwm)
>  {
> -	if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags))
> -		pwm->chip->ops->disable(pwm->chip);
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +
> +	if (test_and_clear_bit(FLAG_ENABLED, &desc->flags))
> +		desc->chip->ops->disable(desc->chip, pwm->pwm);
>  }
>  EXPORT_SYMBOL_GPL(pwm_disable);
> diff --git a/include/linux/pwm.h b/include/linux/pwm.h
> index df9681b..0f8d105 100644
> --- a/include/linux/pwm.h
> +++ b/include/linux/pwm.h
> @@ -32,37 +32,50 @@ void pwm_disable(struct pwm_device *pwm);
>  struct pwm_chip;
>  
>  /**
> - * struct pwm_ops - PWM operations
> + * struct pwm_ops - PWM controller operations
>   * @request: optional hook for requesting a PWM
>   * @free: optional hook for freeing a PWM
>   * @config: configure duty cycles and period length for this PWM
>   * @enable: enable PWM output toggling
>   * @disable: disable PWM output toggling
> + * @owner: helps prevent removal of modules exporting active PWMs
>   */
>  struct pwm_ops {
> -	int			(*request)(struct pwm_chip *chip);
> -	void			(*free)(struct pwm_chip *chip);
> -	int			(*config)(struct pwm_chip *chip, int duty_ns,
> +	int			(*request)(struct pwm_chip *chip,
> +						unsigned int pwm);
> +	void			(*free)(struct pwm_chip *chip,
> +						unsigned int pwm);
> +	int			(*config)(struct pwm_chip *chip,
> +						unsigned int pwm, int duty_ns,
>  						int period_ns);
> -	int			(*enable)(struct pwm_chip *chip);
> -	void			(*disable)(struct pwm_chip *chip);
> +	int			(*enable)(struct pwm_chip *chip,
> +						unsigned int pwm);
> +	void			(*disable)(struct pwm_chip *chip,
> +						unsigned int pwm);
>  	struct module		*owner;
>  };
>  
>  /**
> - * struct pwm_chip - abstract a PWM
> - * @label: for diagnostics
> - * @owner: helps prevent removal of modules exporting active PWMs
> - * @ops: The callbacks for this PWM
> + * struct pwm_chip - abstract a PWM controller
> + * @dev: device providing the PWMs
> + * @ops: callbacks for this PWM controller
> + * @base: number of first PWM controlled by this chip
> + * @npwm: number of PWMs controlled by this chip
>   */
>  struct pwm_chip {
> -	int			pwm_id;
> -	const char		*label;
> +	struct device		*dev;
>  	struct pwm_ops		*ops;
> +	int			base;
> +	unsigned int		npwm;
>  };
>  
> +int pwm_set_chip_data(unsigned int pwm, void *data);
> +void *pwm_get_chip_data(unsigned int pwm);
> +
>  int pwmchip_add(struct pwm_chip *chip);
>  int pwmchip_remove(struct pwm_chip *chip);
> +struct pwm_chip *pwmchip_find(void *data, int (*match)(struct pwm_chip *chip,
> +						       void *data));
>  #endif
>  
>  #endif /* __LINUX_PWM_H */

WARNING: multiple messages have this Message-ID (diff)
From: rmallon@gmail.com (Ryan Mallon)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v2 02/10] pwm: Allow chips to support multiple PWMs.
Date: Wed, 08 Feb 2012 09:53:23 +1100	[thread overview]
Message-ID: <4F31AB63.3020301@gmail.com> (raw)
In-Reply-To: <1328541585-24642-3-git-send-email-thierry.reding@avionic-design.de>

On 07/02/12 02:19, Thierry Reding wrote:

Hi Thierry,

A few comments below,

~Ryan

> This commit modifies the PWM core to support multiple PWMs per struct
> pwm_chip. It achieves this in a similar way to how gpiolib works, by
> allowing PWM ranges to be requested dynamically (pwm_chip.base == -1) or
> starting at a given offset (pwm_chip.base >= 0). A chip specifies how
> many PWMs it controls using the npwm member. Each of the functions in
> the pwm_ops structure gets an additional argument that specified the PWM
> number (it can be converted to a per-chip index by subtracting the
> chip's base).
> 
> The total maximum number of PWM devices is currently fixed to 64, but
> can easily be made configurable via Kconfig.

It would be better to make the code handle arbitrary numbers of PWMs. A
Kconfig knob becomes annoying when you have more than one platform
configured into the kernel.

> The patch is incomplete in that it doesn't convert any existing drivers
> that are now broken.


Does this patch actually break the drivers in terms of not building or
running? If so, can this patch series be reworked a bit to allow the old
PWM framework to be used until all of the drivers are converted?

> 
> Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de>
> ---
> Changes in v2:
>   - add 'struct device *dev' field to pwm_chip, drop label field
>   - use radix tree for PWM descriptors
>   - add pwm_set_chip_data() and pwm_get_chip_data() functions to allow
>     storing and retrieving chip-specific per-PWM data
> 
> TODO:
>   - pass chip-indexed PWM number (PWM descriptor?) to drivers
>   - merge with Sascha's patch
> 
>  drivers/pwm/core.c  |  287 ++++++++++++++++++++++++++++++++++++++-------------
>  include/linux/pwm.h |   37 +++++--
>  2 files changed, 242 insertions(+), 82 deletions(-)
> 
> diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
> index 71de479..a223bd6 100644
> --- a/drivers/pwm/core.c
> +++ b/drivers/pwm/core.c
> @@ -17,154 +17,292 @@
>   *  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/of_pwm.h>
>  #include <linux/pwm.h>
> +#include <linux/radix-tree.h>
>  #include <linux/list.h>
>  #include <linux/mutex.h>
>  #include <linux/err.h>
>  #include <linux/slab.h>
>  #include <linux/device.h>
>  
> +#define MAX_PWMS 1024
> +
>  struct pwm_device {
> -	struct			pwm_chip *chip;
> -	const char		*label;
> +	const char *label;
> +	unsigned int pwm;
> +};
> +
> +struct pwm_desc {
> +	struct pwm_chip		*chip;
> +	void			*chip_data;
>  	unsigned long		flags;
>  #define FLAG_REQUESTED	0
>  #define FLAG_ENABLED	1
> -	struct list_head	node;
> +	struct pwm_device	pwm;
>  };


Do pwm_desc and pwm_device really need to be separate structs?
pwm_device only has two fields, which could easily be added to pwm_desc
(and rename it to pmw_device if you like). They are both opaque
structures, so it makes no difference to the users of the framework.

 
> -static LIST_HEAD(pwm_list);
> -
>  static DEFINE_MUTEX(pwm_lock);
> +static DECLARE_BITMAP(allocated_pwms, MAX_PWMS);
> +static RADIX_TREE(pwm_desc_tree, GFP_KERNEL);

I missed any discussion of this from v1, but why was this approach
chosen? The bitmap arbitrarily limits the maximum number of PWMs and is
also a little bit (nitpicky) wasteful when a platform doesn't need
anywhere near 1024 PWMs.

In most cases I would expect that platforms only have a handful of PWMs
(say less than 32), in which case a list lookup isn't too bad.

As someone else mentioned, it might be best to drop the global numbering
scheme and have each pwm_chip have its own numbering for its PWMs. So
requesting a PWM would first require getting a handle to the PWM chip it
is on. If a chip has a fixed number of PWMs (e.g. set a registration
time) then the PWMs within a chip can just be an array with O(1) lookup.

> +static struct pwm_desc *pwm_to_desc(unsigned int pwm)
> +{
> +	return radix_tree_lookup(&pwm_desc_tree, pwm);
> +}
> +
> +static struct pwm_desc *alloc_desc(unsigned int pwm)
> +{
> +	struct pwm_desc *desc;
> +
> +	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
> +	if (!desc)
> +		return NULL;
>  
> -static struct pwm_device *_find_pwm(int pwm_id)
> +	return desc;
> +}
> +
> +static void free_desc(unsigned int pwm)
> +{
> +	struct pwm_desc *desc = pwm_to_desc(pwm);
> +
> +	radix_tree_delete(&pwm_desc_tree, pwm);
> +	kfree(desc);
> +}
> +
> +static int alloc_descs(int pwm, unsigned int from, unsigned int count)

>  {

You don't really need the from argument. There is only one caller for
alloc_descs and it passes 0 for from. So you could re-write this as:

	static int alloc_descs(int pwm, unsigned int count)
	{
		unsigned int from = 0;
		...

		if (pwm > MAX_PWMS)
			return -EINVAL;
		if (pwm >= 0)
			from = pwm;

> -	struct pwm_device *pwm;
> +	unsigned int start;
> +	unsigned int i;
> +	int ret = 0;
> +
> +	if (pwm >= 0) {
> +		if (from > pwm)
> +			return -EINVAL;
> +
> +		from = pwm;
> +	}


This doesn't catch some caller errors:

	if (pwm > MAX_PWMS || from > MAX_PWMS)
		return -EINVAL;

> +
> +	start = bitmap_find_next_zero_area(allocated_pwms, MAX_PWMS, from,
> +			count, 0);

Do the PWM indexes need to be contiguous for a given PWM chip? You could
probably grab each bit individually. That said, I think a better
solution is to have per-chip numbering.

> -	list_for_each_entry(pwm, &pwm_list, node) {
> -		if (pwm->chip->pwm_id == pwm_id)
> -			return pwm;
> +	if ((pwm >= 0) && (start != pwm))


Don't need the inner parens.

> +		return -EEXIST;
> +
> +	if (start + count > MAX_PWMS)
> +		return -ENOSPC;


Is this possible? Can bitmap_find_next_zero_area return a start position
where the count would exceed the maximum?

> +
> +	for (i = start; i < start + count; i++) {
> +		struct pwm_desc *desc = alloc_desc(i);
> +		if (!desc) {
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +
> +		radix_tree_insert(&pwm_desc_tree, i, desc);
>  	}
>  
> -	return NULL;
> +	bitmap_set(allocated_pwms, start, count);
> +
> +	return start;
> +
> +err:
> +	for (i--; i >= 0; i--)


Nitpick:

	while (--i >= 0)

is a bit simpler.

> +		free_desc(i);
> +
> +	return ret;
> +}
> +
> +static void free_descs(unsigned int from, unsigned int count)
> +{
> +	unsigned int i;
> +
> +	for (i = from; i < from + count; i++)
> +		free_desc(i);
> +
> +	bitmap_clear(allocated_pwms, from, count);
>  }
>  
>  /**
> - * pwmchip_add() - register a new pwm
> - * @chip: the pwm
> - *
> - * register a new pwm. pwm->pwm_id must be initialized. if pwm_id < 0 then
> - * a dynamically assigned id will be used, otherwise the id specified,
> + * pwm_set_chip_data - set private chip data for a PWM
> + * @pwm: PWM number
> + * @data: pointer to chip-specific data
>   */
> -int pwmchip_add(struct pwm_chip *chip)
> +int pwm_set_chip_data(unsigned int pwm, void *data)


This interface is a bit ugly. Shouldn't the user need to have a handle
to the pwm_device in order to access the chip_data? Why should callers
be able to set/grab chip data from random PWMs?

>  {
> -	struct pwm_device *pwm;
> -	int ret = 0;
> +	struct pwm_desc *desc = pwm_to_desc(pwm);
>  
> -	pwm = kzalloc(sizeof(*pwm), GFP_KERNEL);
> -	if (!pwm)
> -		return -ENOMEM;
> +	if (!desc)
> +		return -EINVAL;
>  
> -	pwm->chip = chip;
> +	desc->chip_data = data;
> +	return 0;
> +}
> +
> +/**
> + * pwm_get_chip_data - get private chip data for a PWM
> + * @pwm: PWM number
> + */
> +void *pwm_get_chip_data(unsigned int pwm)
> +{

> +	struct pwm_desc *desc = pwm_to_desc(pwm);
> +
> +	return desc ? desc->chip_data : NULL;
> +}
> +
> +/**
> + * pwmchip_add() - register a new PWM chip
> + * @chip: the PWM chip to add
> + *
> + * Register a new PWM chip. If pwm->base < 0 then a dynamically assigned base
> + * will be used.
> + */
> +int pwmchip_add(struct pwm_chip *chip)
> +{
> +	unsigned int i;
> +	int ret;
>  
>  	mutex_lock(&pwm_lock);
>  
> -	if (chip->pwm_id >= 0 && _find_pwm(chip->pwm_id)) {
> -		ret = -EBUSY;
> +	ret = alloc_descs(chip->base, 0, chip->npwm);
> +	if (ret < 0)
>  		goto out;
> +
> +	chip->base = ret;
> +	ret = 0;
> +
> +	/* initialize descriptors */
> +	for (i = chip->base; i < chip->base + chip->npwm; i++) {
> +		struct pwm_desc *desc = pwm_to_desc(i);
> +
> +		if (!desc) {
> +			pr_debug("pwm: descriptor %u not initialized\n", i);


Should this be a WARN_ON?

> +			continue;
> +		}
> +
> +		desc->chip = chip;
>  	}
>  
> -	list_add_tail(&pwm->node, &pwm_list);
> +	of_pwmchip_add(chip);
> +
>  out:
>  	mutex_unlock(&pwm_lock);
> -
> -	if (ret)
> -		kfree(pwm);
> -
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(pwmchip_add);
>  
>  /**
> - * pwmchip_remove() - remove a pwm
> - * @chip: the pwm
> + * pwmchip_remove() - remove a PWM chip
> + * @chip: the PWM chip to remove
>   *
> - * remove a pwm. This function may return busy if the pwm is still requested.
> + * 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;
> +	unsigned int i;
>  	int ret = 0;
>  
>  	mutex_lock(&pwm_lock);
>  
> -	pwm = _find_pwm(chip->pwm_id);
> -	if (!pwm) {
> -		ret = -ENOENT;
> -		goto out;
> -	}
> +	for (i = chip->base; i < chip->base + chip->npwm; i++) {
> +		struct pwm_desc *desc = pwm_to_desc(i);
>  
> -	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
> -		ret = -EBUSY;
> -		goto out;
> +		if (test_bit(FLAG_REQUESTED, &desc->flags)) {
> +			ret = -EBUSY;
> +			goto out;
> +		}
>  	}
>  
> -	list_del(&pwm->node);
> +	free_descs(chip->base, chip->npwm);
> +	of_pwmchip_remove(chip);
>  
> -	kfree(pwm);
>  out:
>  	mutex_unlock(&pwm_lock);
> -
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(pwmchip_remove);
>  
> +/**
> + * pwmchip_find() - iterator for locating a specific pwm_chip
> + * @data: data to pass to match function
> + * @match: callback function to check pwm_chip
> + */
> +struct pwm_chip *pwmchip_find(void *data, int (*match)(struct pwm_chip *chip,
> +						       void *data))
> +{
> +	struct pwm_chip *chip = NULL;
> +	unsigned long i;
> +
> +	mutex_lock(&pwm_lock);
> +
> +	for_each_set_bit(i, allocated_pwms, MAX_PWMS) {
> +		struct pwm_desc *desc = pwm_to_desc(i);
> +
> +		if (!desc || !desc->chip)
> +			continue;
> +
> +		if (match(desc->chip, data)) {
> +			chip = desc->chip;
> +			break;
> +		}
> +	}
> +
> +	mutex_unlock(&pwm_lock);
> +
> +	return chip;
> +}
> +EXPORT_SYMBOL_GPL(pwmchip_find);


So, propreitary modules are not allowed to use PWMs?

> +
>  /*
>   * pwm_request - request a PWM device
>   */
> -struct pwm_device *pwm_request(int pwm_id, const char *label)
> +struct pwm_device *pwm_request(int pwm, const char *label)
>  {
> -	struct pwm_device *pwm;
> +	struct pwm_device *dev;
> +	struct pwm_desc *desc;
>  	int ret;
>  
> +	if ((pwm < 0) || (pwm >= MAX_PWMS))


Don't need the inner parens.

> +		return ERR_PTR(-ENOENT);


-EINVAL maybe?

> +
>  	mutex_lock(&pwm_lock);
>  
> -	pwm = _find_pwm(pwm_id);
> -	if (!pwm) {
> -		pwm = ERR_PTR(-ENOENT);
> -		goto out;
> -	}
> +	desc = pwm_to_desc(pwm);
> +	dev = &desc->pwm;


This looks wrong. If the pwm_desc being requested doesn't exist, won't
this oops?

>  
> -	if (test_bit(FLAG_REQUESTED, &pwm->flags)) {
> -		pwm = ERR_PTR(-EBUSY);
> +	if (test_bit(FLAG_REQUESTED, &desc->flags)) {
> +		dev = ERR_PTR(-EBUSY);
>  		goto out;
>  	}
>  
> -	if (!try_module_get(pwm->chip->ops->owner)) {
> -		pwm = ERR_PTR(-ENODEV);
> +	if (!try_module_get(desc->chip->ops->owner)) {
> +		dev = ERR_PTR(-ENODEV);
>  		goto out;
>  	}
>  
> -	if (pwm->chip->ops->request) {
> -		ret = pwm->chip->ops->request(pwm->chip);
> +	if (desc->chip->ops->request) {
> +		ret = desc->chip->ops->request(desc->chip, pwm);
>  		if (ret) {
> -			pwm = ERR_PTR(ret);
> +			dev = ERR_PTR(ret);
>  			goto out_put;
>  		}
>  	}
>  
> -	pwm->label = label;
> -	set_bit(FLAG_REQUESTED, &pwm->flags);
> +	dev->label = label;
> +	dev->pwm = pwm;
> +	set_bit(FLAG_REQUESTED, &desc->flags);
>  
>  	goto out;
>  
>  out_put:
> -	module_put(pwm->chip->ops->owner);
> +	module_put(desc->chip->ops->owner);
>  out:
>  	mutex_unlock(&pwm_lock);
>  
> -	return pwm;
> +	return dev;
>  }
>  EXPORT_SYMBOL_GPL(pwm_request);
>  
> @@ -173,16 +311,19 @@ EXPORT_SYMBOL_GPL(pwm_request);
>   */
>  void pwm_free(struct pwm_device *pwm)
>  {
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +
>  	mutex_lock(&pwm_lock);

pwm_lock is used to protect global addition and removal from the
radix/tree bitmap. Here you are also using it to protect the fields of a
specific pwm device? Maybe it would be better to have a per pwm device
lock for this?

> -	if (!test_and_clear_bit(FLAG_REQUESTED, &pwm->flags)) {
> +	if (!test_and_clear_bit(FLAG_REQUESTED, &desc->flags)) {
>  		pr_warning("PWM device already freed\n");
>  		goto out;
>  	}
>  
>  	pwm->label = NULL;
> +	pwm->pwm = 0;


Why do this? Isn't index 0 a valid PWM index?a

>  
> -	module_put(pwm->chip->ops->owner);
> +	module_put(desc->chip->ops->owner);
>  out:
>  	mutex_unlock(&pwm_lock);
>  }
> @@ -193,7 +334,9 @@ EXPORT_SYMBOL_GPL(pwm_free);
>   */
>  int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
>  {
> -	return pwm->chip->ops->config(pwm->chip, duty_ns, period_ns);
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +	return desc->chip->ops->config(desc->chip, pwm->pwm, duty_ns,
> +			period_ns);
>  }
>  EXPORT_SYMBOL_GPL(pwm_config);
>  
> @@ -202,8 +345,10 @@ EXPORT_SYMBOL_GPL(pwm_config);
>   */
>  int pwm_enable(struct pwm_device *pwm)
>  {
> -	if (!test_and_set_bit(FLAG_ENABLED, &pwm->flags))
> -		return pwm->chip->ops->enable(pwm->chip);
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +
> +	if (!test_and_set_bit(FLAG_ENABLED, &desc->flags))
> +		return desc->chip->ops->enable(desc->chip, pwm->pwm);
>  
>  	return 0;
>  }
> @@ -214,7 +359,9 @@ EXPORT_SYMBOL_GPL(pwm_enable);
>   */
>  void pwm_disable(struct pwm_device *pwm)
>  {
> -	if (test_and_clear_bit(FLAG_ENABLED, &pwm->flags))
> -		pwm->chip->ops->disable(pwm->chip);
> +	struct pwm_desc *desc = container_of(pwm, struct pwm_desc, pwm);
> +
> +	if (test_and_clear_bit(FLAG_ENABLED, &desc->flags))
> +		desc->chip->ops->disable(desc->chip, pwm->pwm);
>  }
>  EXPORT_SYMBOL_GPL(pwm_disable);
> diff --git a/include/linux/pwm.h b/include/linux/pwm.h
> index df9681b..0f8d105 100644
> --- a/include/linux/pwm.h
> +++ b/include/linux/pwm.h
> @@ -32,37 +32,50 @@ void pwm_disable(struct pwm_device *pwm);
>  struct pwm_chip;
>  
>  /**
> - * struct pwm_ops - PWM operations
> + * struct pwm_ops - PWM controller operations
>   * @request: optional hook for requesting a PWM
>   * @free: optional hook for freeing a PWM
>   * @config: configure duty cycles and period length for this PWM
>   * @enable: enable PWM output toggling
>   * @disable: disable PWM output toggling
> + * @owner: helps prevent removal of modules exporting active PWMs
>   */
>  struct pwm_ops {
> -	int			(*request)(struct pwm_chip *chip);
> -	void			(*free)(struct pwm_chip *chip);
> -	int			(*config)(struct pwm_chip *chip, int duty_ns,
> +	int			(*request)(struct pwm_chip *chip,
> +						unsigned int pwm);
> +	void			(*free)(struct pwm_chip *chip,
> +						unsigned int pwm);
> +	int			(*config)(struct pwm_chip *chip,
> +						unsigned int pwm, int duty_ns,
>  						int period_ns);
> -	int			(*enable)(struct pwm_chip *chip);
> -	void			(*disable)(struct pwm_chip *chip);
> +	int			(*enable)(struct pwm_chip *chip,
> +						unsigned int pwm);
> +	void			(*disable)(struct pwm_chip *chip,
> +						unsigned int pwm);
>  	struct module		*owner;
>  };
>  
>  /**
> - * struct pwm_chip - abstract a PWM
> - * @label: for diagnostics
> - * @owner: helps prevent removal of modules exporting active PWMs
> - * @ops: The callbacks for this PWM
> + * struct pwm_chip - abstract a PWM controller
> + * @dev: device providing the PWMs
> + * @ops: callbacks for this PWM controller
> + * @base: number of first PWM controlled by this chip
> + * @npwm: number of PWMs controlled by this chip
>   */
>  struct pwm_chip {
> -	int			pwm_id;
> -	const char		*label;
> +	struct device		*dev;
>  	struct pwm_ops		*ops;
> +	int			base;
> +	unsigned int		npwm;
>  };
>  
> +int pwm_set_chip_data(unsigned int pwm, void *data);
> +void *pwm_get_chip_data(unsigned int pwm);
> +
>  int pwmchip_add(struct pwm_chip *chip);
>  int pwmchip_remove(struct pwm_chip *chip);
> +struct pwm_chip *pwmchip_find(void *data, int (*match)(struct pwm_chip *chip,
> +						       void *data));
>  #endif
>  
>  #endif /* __LINUX_PWM_H */

  parent reply	other threads:[~2012-02-07 22:53 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-06 15:19 [PATCH v2 00/10] Add PWM framework and device-tree support Thierry Reding
2012-02-06 15:19 ` Thierry Reding
     [not found] ` <1328541585-24642-1-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-02-06 15:19   ` [PATCH v2 01/10] PWM: add pwm framework support Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 02/10] pwm: Allow chips to support multiple PWMs Thierry Reding
2012-02-06 15:19     ` Thierry Reding
     [not found]     ` <1328541585-24642-3-git-send-email-thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org>
2012-02-06 21:22       ` Lars-Peter Clausen
2012-02-06 21:22         ` Lars-Peter Clausen
     [not found]         ` <4F3044A9.8000202-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
2012-02-07  7:04           ` Thierry Reding
2012-02-07  7:04             ` Thierry Reding
     [not found]             ` <20120207070400.GA29238-RM9K5IK7kjIQXX3q8xo1gnVAuStQJXxyR5q1nwbD4aMs9pC9oP6+/A@public.gmane.org>
2012-02-07 11:38               ` Mark Brown
2012-02-07 11:38                 ` Mark Brown
2012-02-08  9:13               ` Russell King - ARM Linux
2012-02-08  9:13                 ` Russell King - ARM Linux
     [not found]                 ` <20120208091327.GH889-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2012-02-08 11:12                   ` Thierry Reding
2012-02-08 11:12                     ` Thierry Reding
2012-02-07 22:53       ` Ryan Mallon [this message]
2012-02-07 22:53         ` Ryan Mallon
     [not found]         ` <4F31AB63.3020301-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-02-08  8:15           ` Thierry Reding
2012-02-08  8:15             ` Thierry Reding
     [not found]             ` <20120208081508.GA6673-RM9K5IK7kjIQXX3q8xo1gnVAuStQJXxyR5q1nwbD4aMs9pC9oP6+/A@public.gmane.org>
2012-02-08  9:00               ` Sascha Hauer
2012-02-08  9:00                 ` Sascha Hauer
     [not found]                 ` <20120208090055.GP3852-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2012-02-08 11:16                   ` Thierry Reding
2012-02-08 11:16                     ` Thierry Reding
2012-02-08  9:17               ` Russell King - ARM Linux
2012-02-08  9:17                 ` Russell King - ARM Linux
     [not found]                 ` <20120208091720.GI889-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2012-02-08 10:31                   ` Thierry Reding
2012-02-08 10:31                     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 03/10] of: Add PWM support Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 04/10] arm/tegra: Fix PWM clock programming Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 05/10] arm/tegra: Provide clock for only one PWM controller Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 06/10] pwm: Add NVIDIA Tegra SoC support Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 07/10] arm/tegra: Add PWFM controller device tree probing Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 08/10] pwm: Add Blackfin support Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 09/10] pwm: Add PXA support Thierry Reding
2012-02-06 15:19     ` Thierry Reding
2012-02-06 15:19   ` [PATCH v2 10/10] pwm-backlight: Add rudimentary device tree support Thierry Reding
2012-02-06 15:19     ` Thierry Reding

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4F31AB63.3020301@gmail.com \
    --to=rmallon-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
    --cc=arnd-r2nGTMty4D4@public.gmane.org \
    --cc=broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org \
    --cc=ccross-z5hGa2qSFaRBDgjK7y7TUQ@public.gmane.org \
    --cc=devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org \
    --cc=eric.y.miao-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org \
    --cc=kurt.van.dijck-/BeEPy95v10@public.gmane.org \
    --cc=linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org \
    --cc=linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=matthias-RprLehDfhQ3k1uMJSBkQmQ@public.gmane.org \
    --cc=olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org \
    --cc=rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org \
    --cc=rpurdie-Fm38FmjxZ/leoWH0uzbU5w@public.gmane.org \
    --cc=s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
    --cc=thierry.reding-RM9K5IK7kjKj5M59NBduVrNAH6kLmebB@public.gmane.org \
    --cc=vapier-aBrp7R+bbdUdnm+yROfE0A@public.gmane.org \
    --cc=wmb-D5eQfiDGL7eakBO8gow8eQ@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.