mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			266 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From d25cfe9b4f9663216ce4e011e3f1e7fa669ab58a Mon Sep 17 00:00:00 2001
 | |
| From: Hans de Goede <hdegoede@redhat.com>
 | |
| Date: Fri, 27 Nov 2015 21:09:05 +0100
 | |
| Subject: [PATCH] reset: Add shared reset_control_[de]assert variants
 | |
| 
 | |
| Add reset_control_deassert_shared / reset_control_assert_shared
 | |
| functions which are intended for use by drivers for hw blocks which
 | |
| (may) share a reset line with another driver / hw block.
 | |
| 
 | |
| Unlike the regular reset_control_[de]assert functions these functions
 | |
| keep track of how often deassert_shared / assert_shared have been called
 | |
| and keep the line deasserted as long as deassert has been called more
 | |
| times than assert.
 | |
| 
 | |
| Signed-off-by: Hans de Goede <hdegoede@redhat.com>
 | |
| ---
 | |
| Changes in v2:
 | |
| -This is a new patch in v2 of this patch-set
 | |
| ---
 | |
|  drivers/reset/core.c             | 121 ++++++++++++++++++++++++++++++++++++---
 | |
|  include/linux/reset-controller.h |   2 +
 | |
|  include/linux/reset.h            |   2 +
 | |
|  3 files changed, 116 insertions(+), 9 deletions(-)
 | |
| 
 | |
| --- a/drivers/reset/core.c
 | |
| +++ b/drivers/reset/core.c
 | |
| @@ -22,16 +22,29 @@ static DEFINE_MUTEX(reset_controller_lis
 | |
|  static LIST_HEAD(reset_controller_list);
 | |
|  
 | |
|  /**
 | |
| + * struct reset_line - a reset line
 | |
| + * @list:         list entry for the reset controllers reset line list
 | |
| + * @id:           ID of the reset line in the reset controller device
 | |
| + * @refcnt:       Number of reset_control structs referencing this device
 | |
| + * @deassert_cnt: Number of times this reset line has been deasserted
 | |
| + */
 | |
| +struct reset_line {
 | |
| +	struct list_head list;
 | |
| +	unsigned int id;
 | |
| +	unsigned int refcnt;
 | |
| +	unsigned int deassert_cnt;
 | |
| +};
 | |
| +
 | |
| +/**
 | |
|   * struct reset_control - a reset control
 | |
|   * @rcdev: a pointer to the reset controller device
 | |
|   *         this reset control belongs to
 | |
| - * @id: ID of the reset controller in the reset
 | |
| - *      controller device
 | |
| + * @line:  reset line for this reset control
 | |
|   */
 | |
|  struct reset_control {
 | |
|  	struct reset_controller_dev *rcdev;
 | |
| +	struct reset_line *line;
 | |
|  	struct device *dev;
 | |
| -	unsigned int id;
 | |
|  };
 | |
|  
 | |
|  /**
 | |
| @@ -66,6 +79,8 @@ int reset_controller_register(struct res
 | |
|  		rcdev->of_xlate = of_reset_simple_xlate;
 | |
|  	}
 | |
|  
 | |
| +	INIT_LIST_HEAD(&rcdev->reset_line_head);
 | |
| +
 | |
|  	mutex_lock(&reset_controller_list_mutex);
 | |
|  	list_add(&rcdev->list, &reset_controller_list);
 | |
|  	mutex_unlock(&reset_controller_list_mutex);
 | |
| @@ -93,7 +108,7 @@ EXPORT_SYMBOL_GPL(reset_controller_unreg
 | |
|  int reset_control_reset(struct reset_control *rstc)
 | |
|  {
 | |
|  	if (rstc->rcdev->ops->reset)
 | |
| -		return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id);
 | |
| +		return rstc->rcdev->ops->reset(rstc->rcdev, rstc->line->id);
 | |
|  
 | |
|  	return -ENOTSUPP;
 | |
|  }
 | |
| @@ -106,7 +121,7 @@ EXPORT_SYMBOL_GPL(reset_control_reset);
 | |
|  int reset_control_assert(struct reset_control *rstc)
 | |
|  {
 | |
|  	if (rstc->rcdev->ops->assert)
 | |
| -		return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id);
 | |
| +		return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id);
 | |
|  
 | |
|  	return -ENOTSUPP;
 | |
|  }
 | |
| @@ -119,13 +134,55 @@ EXPORT_SYMBOL_GPL(reset_control_assert);
 | |
|  int reset_control_deassert(struct reset_control *rstc)
 | |
|  {
 | |
|  	if (rstc->rcdev->ops->deassert)
 | |
| -		return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id);
 | |
| +		return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id);
 | |
|  
 | |
|  	return -ENOTSUPP;
 | |
|  }
 | |
|  EXPORT_SYMBOL_GPL(reset_control_deassert);
 | |
|  
 | |
|  /**
 | |
| + * reset_control_assert_shared - asserts a shared reset line
 | |
| + * @rstc: reset controller
 | |
| + *
 | |
| + * Assert a shared reset line, this functions decreases the deassert count
 | |
| + * of the line by one and asserts it if, and only if, the deassert count
 | |
| + * reaches 0.
 | |
| + */
 | |
| +int reset_control_assert_shared(struct reset_control *rstc)
 | |
| +{
 | |
| +	if (!rstc->rcdev->ops->assert)
 | |
| +		return -ENOTSUPP;
 | |
| +
 | |
| +	rstc->line->deassert_cnt--;
 | |
| +	if (rstc->line->deassert_cnt)
 | |
| +		return 0;
 | |
| +
 | |
| +	return rstc->rcdev->ops->assert(rstc->rcdev, rstc->line->id);
 | |
| +}
 | |
| +EXPORT_SYMBOL_GPL(reset_control_assert_shared);
 | |
| +
 | |
| +/**
 | |
| + * reset_control_deassert_shared - deasserts a shared reset line
 | |
| + * @rstc: reset controller
 | |
| + *
 | |
| + * Assert a shared reset line, this functions increases the deassert count
 | |
| + * of the line by one and deasserts the reset line (if it was not already
 | |
| + * deasserted).
 | |
| + */
 | |
| +int reset_control_deassert_shared(struct reset_control *rstc)
 | |
| +{
 | |
| +	if (!rstc->rcdev->ops->deassert)
 | |
| +		return -ENOTSUPP;
 | |
| +
 | |
| +	rstc->line->deassert_cnt++;
 | |
| +	if (rstc->line->deassert_cnt != 1)
 | |
| +		return 0;
 | |
| +
 | |
| +	return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->line->id);
 | |
| +}
 | |
| +EXPORT_SYMBOL_GPL(reset_control_deassert_shared);
 | |
| +
 | |
| +/**
 | |
|   * reset_control_status - returns a negative errno if not supported, a
 | |
|   * positive value if the reset line is asserted, or zero if the reset
 | |
|   * line is not asserted.
 | |
| @@ -134,12 +191,47 @@ EXPORT_SYMBOL_GPL(reset_control_deassert
 | |
|  int reset_control_status(struct reset_control *rstc)
 | |
|  {
 | |
|  	if (rstc->rcdev->ops->status)
 | |
| -		return rstc->rcdev->ops->status(rstc->rcdev, rstc->id);
 | |
| +		return rstc->rcdev->ops->status(rstc->rcdev, rstc->line->id);
 | |
|  
 | |
|  	return -ENOTSUPP;
 | |
|  }
 | |
|  EXPORT_SYMBOL_GPL(reset_control_status);
 | |
|  
 | |
| +static struct reset_line *reset_line_get(struct reset_controller_dev *rcdev,
 | |
| +					 unsigned int index)
 | |
| +{
 | |
| +	struct reset_line *line;
 | |
| +
 | |
| +	list_for_each_entry(line, &rcdev->reset_line_head, list) {
 | |
| +		if (line->id == index) {
 | |
| +			line->refcnt++;
 | |
| +			return line;
 | |
| +		}
 | |
| +	}
 | |
| +
 | |
| +	line = kzalloc(sizeof(*line), GFP_KERNEL);
 | |
| +	if (!line)
 | |
| +		return NULL;
 | |
| +
 | |
| +	list_add(&line->list, &rcdev->reset_line_head);
 | |
| +	line->id = index;
 | |
| +	line->refcnt = 1;
 | |
| +
 | |
| +	return line;
 | |
| +}
 | |
| +
 | |
| +static void reset_line_put(struct reset_line *line)
 | |
| +{
 | |
| +	if (!line)
 | |
| +		return;
 | |
| +
 | |
| +	if (--line->refcnt)
 | |
| +		return;
 | |
| +
 | |
| +	list_del(&line->list);
 | |
| +	kfree(line);
 | |
| +}
 | |
| +
 | |
|  /**
 | |
|   * of_reset_control_get_by_index - Lookup and obtain a reference to a reset
 | |
|   * controller by index.
 | |
| @@ -155,6 +247,7 @@ struct reset_control *of_reset_control_g
 | |
|  {
 | |
|  	struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER);
 | |
|  	struct reset_controller_dev *r, *rcdev;
 | |
| +	struct reset_line *line;
 | |
|  	struct of_phandle_args args;
 | |
|  	int rstc_id;
 | |
|  	int ret;
 | |
| @@ -186,16 +279,22 @@ struct reset_control *of_reset_control_g
 | |
|  	}
 | |
|  
 | |
|  	try_module_get(rcdev->owner);
 | |
| +
 | |
| +	/* reset_controller_list_mutex also protects the reset_line list */
 | |
| +	line = reset_line_get(rcdev, rstc_id);
 | |
| +
 | |
|  	mutex_unlock(&reset_controller_list_mutex);
 | |
|  
 | |
|  	rstc = kzalloc(sizeof(*rstc), GFP_KERNEL);
 | |
| -	if (!rstc) {
 | |
| +	if (!line || !rstc) {
 | |
| +		kfree(rstc);
 | |
| +		reset_line_put(line);
 | |
|  		module_put(rcdev->owner);
 | |
|  		return ERR_PTR(-ENOMEM);
 | |
|  	}
 | |
|  
 | |
|  	rstc->rcdev = rcdev;
 | |
| -	rstc->id = rstc_id;
 | |
| +	rstc->line = line;
 | |
|  
 | |
|  	return rstc;
 | |
|  }
 | |
| @@ -259,6 +358,10 @@ void reset_control_put(struct reset_cont
 | |
|  	if (IS_ERR(rstc))
 | |
|  		return;
 | |
|  
 | |
| +	mutex_lock(&reset_controller_list_mutex);
 | |
| +	reset_line_put(rstc->line);
 | |
| +	mutex_unlock(&reset_controller_list_mutex);
 | |
| +
 | |
|  	module_put(rstc->rcdev->owner);
 | |
|  	kfree(rstc);
 | |
|  }
 | |
| --- a/include/linux/reset-controller.h
 | |
| +++ b/include/linux/reset-controller.h
 | |
| @@ -31,6 +31,7 @@ struct of_phandle_args;
 | |
|   * @ops: a pointer to device specific struct reset_control_ops
 | |
|   * @owner: kernel module of the reset controller driver
 | |
|   * @list: internal list of reset controller devices
 | |
| + * @reset_line_head: head of internal list of reset lines
 | |
|   * @of_node: corresponding device tree node as phandle target
 | |
|   * @of_reset_n_cells: number of cells in reset line specifiers
 | |
|   * @of_xlate: translation function to translate from specifier as found in the
 | |
| @@ -41,6 +42,7 @@ struct reset_controller_dev {
 | |
|  	struct reset_control_ops *ops;
 | |
|  	struct module *owner;
 | |
|  	struct list_head list;
 | |
| +	struct list_head reset_line_head;
 | |
|  	struct device_node *of_node;
 | |
|  	int of_reset_n_cells;
 | |
|  	int (*of_xlate)(struct reset_controller_dev *rcdev,
 | |
| --- a/include/linux/reset.h
 | |
| +++ b/include/linux/reset.h
 | |
| @@ -11,6 +11,8 @@ int reset_control_reset(struct reset_con
 | |
|  int reset_control_assert(struct reset_control *rstc);
 | |
|  int reset_control_deassert(struct reset_control *rstc);
 | |
|  int reset_control_status(struct reset_control *rstc);
 | |
| +int reset_control_assert_shared(struct reset_control *rstc);
 | |
| +int reset_control_deassert_shared(struct reset_control *rstc);
 | |
|  
 | |
|  struct reset_control *reset_control_get(struct device *dev, const char *id);
 | |
|  void reset_control_put(struct reset_control *rstc);
 |