diff options
Diffstat (limited to 'drivers/clk/clkdev.c')
| -rw-r--r-- | drivers/clk/clkdev.c | 231 | 
1 files changed, 101 insertions, 130 deletions
| diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 9ab3db8b3988..8c4435c53f09 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -27,99 +27,6 @@  static LIST_HEAD(clocks);  static DEFINE_MUTEX(clocks_mutex); -#if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) -static struct clk *__of_clk_get(struct device_node *np, int index, -			       const char *dev_id, const char *con_id) -{ -	struct of_phandle_args clkspec; -	struct clk *clk; -	int rc; - -	rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, -					&clkspec); -	if (rc) -		return ERR_PTR(rc); - -	clk = __of_clk_get_from_provider(&clkspec, dev_id, con_id); -	of_node_put(clkspec.np); - -	return clk; -} - -struct clk *of_clk_get(struct device_node *np, int index) -{ -	return __of_clk_get(np, index, np->full_name, NULL); -} -EXPORT_SYMBOL(of_clk_get); - -static struct clk *__of_clk_get_by_name(struct device_node *np, -					const char *dev_id, -					const char *name) -{ -	struct clk *clk = ERR_PTR(-ENOENT); - -	/* Walk up the tree of devices looking for a clock that matches */ -	while (np) { -		int index = 0; - -		/* -		 * For named clocks, first look up the name in the -		 * "clock-names" property.  If it cannot be found, then -		 * index will be an error code, and of_clk_get() will fail. -		 */ -		if (name) -			index = of_property_match_string(np, "clock-names", name); -		clk = __of_clk_get(np, index, dev_id, name); -		if (!IS_ERR(clk)) { -			break; -		} else if (name && index >= 0) { -			if (PTR_ERR(clk) != -EPROBE_DEFER) -				pr_err("ERROR: could not get clock %pOF:%s(%i)\n", -					np, name ? name : "", index); -			return clk; -		} - -		/* -		 * No matching clock found on this node.  If the parent node -		 * has a "clock-ranges" property, then we can try one of its -		 * clocks. -		 */ -		np = np->parent; -		if (np && !of_get_property(np, "clock-ranges", NULL)) -			break; -	} - -	return clk; -} - -/** - * of_clk_get_by_name() - Parse and lookup a clock referenced by a device node - * @np: pointer to clock consumer node - * @name: name of consumer's clock input, or NULL for the first clock reference - * - * This function parses the clocks and clock-names properties, - * and uses them to look up the struct clk from the registered list of clock - * providers. - */ -struct clk *of_clk_get_by_name(struct device_node *np, const char *name) -{ -	if (!np) -		return ERR_PTR(-ENOENT); - -	return __of_clk_get_by_name(np, np->full_name, name); -} -EXPORT_SYMBOL(of_clk_get_by_name); - -#else /* defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) */ - -static struct clk *__of_clk_get_by_name(struct device_node *np, -					const char *dev_id, -					const char *name) -{ -	return ERR_PTR(-ENOENT); -} -#endif -  /*   * Find the correct struct clk for the device and connection ID.   * We do slightly fuzzy matching here: @@ -163,7 +70,8 @@ static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)  	return cl;  } -struct clk *clk_get_sys(const char *dev_id, const char *con_id) +static struct clk *__clk_get_sys(struct device *dev, const char *dev_id, +				 const char *con_id)  {  	struct clk_lookup *cl;  	struct clk *clk = NULL; @@ -174,35 +82,33 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)  	if (!cl)  		goto out; -	clk = __clk_create_clk(cl->clk_hw, dev_id, con_id); +	clk = clk_hw_create_clk(dev, cl->clk_hw, dev_id, con_id);  	if (IS_ERR(clk)) -		goto out; - -	if (!__clk_get(clk)) { -		__clk_free_clk(clk);  		cl = NULL; -		goto out; -	} -  out:  	mutex_unlock(&clocks_mutex);  	return cl ? clk : ERR_PTR(-ENOENT);  } + +struct clk *clk_get_sys(const char *dev_id, const char *con_id) +{ +	return __clk_get_sys(NULL, dev_id, con_id); +}  EXPORT_SYMBOL(clk_get_sys);  struct clk *clk_get(struct device *dev, const char *con_id)  {  	const char *dev_id = dev ? dev_name(dev) : NULL; -	struct clk *clk; +	struct clk_hw *hw;  	if (dev && dev->of_node) { -		clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id); -		if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER) -			return clk; +		hw = of_clk_get_hw(dev->of_node, 0, con_id); +		if (!IS_ERR(hw) || PTR_ERR(hw) == -EPROBE_DEFER) +			return clk_hw_create_clk(dev, hw, dev_id, con_id);  	} -	return clk_get_sys(dev_id, con_id); +	return __clk_get_sys(dev, dev_id, con_id);  }  EXPORT_SYMBOL(clk_get); @@ -401,6 +307,23 @@ static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,  	return cl;  } +static int do_clk_register_clkdev(struct clk_hw *hw, +	struct clk_lookup **cl, const char *con_id, const char *dev_id) +{ +	if (IS_ERR(hw)) +		return PTR_ERR(hw); +	/* +	 * Since dev_id can be NULL, and NULL is handled specially, we must +	 * pass it as either a NULL format string, or with "%s". +	 */ +	if (dev_id) +		*cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); +	else +		*cl = __clk_register_clkdev(hw, con_id, NULL); + +	return *cl ? 0 : -ENOMEM; +} +  /**   * clk_register_clkdev - register one clock lookup for a struct clk   * @clk: struct clk to associate with all clk_lookups @@ -423,17 +346,8 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,  	if (IS_ERR(clk))  		return PTR_ERR(clk); -	/* -	 * Since dev_id can be NULL, and NULL is handled specially, we must -	 * pass it as either a NULL format string, or with "%s". -	 */ -	if (dev_id) -		cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s", -					   dev_id); -	else -		cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL); - -	return cl ? 0 : -ENOMEM; +	return do_clk_register_clkdev(__clk_get_hw(clk), &cl, con_id, +					      dev_id);  }  EXPORT_SYMBOL(clk_register_clkdev); @@ -456,18 +370,75 @@ int clk_hw_register_clkdev(struct clk_hw *hw, const char *con_id,  {  	struct clk_lookup *cl; -	if (IS_ERR(hw)) -		return PTR_ERR(hw); +	return do_clk_register_clkdev(hw, &cl, con_id, dev_id); +} +EXPORT_SYMBOL(clk_hw_register_clkdev); -	/* -	 * Since dev_id can be NULL, and NULL is handled specially, we must -	 * pass it as either a NULL format string, or with "%s". -	 */ -	if (dev_id) -		cl = __clk_register_clkdev(hw, con_id, "%s", dev_id); -	else -		cl = __clk_register_clkdev(hw, con_id, NULL); +static void devm_clkdev_release(struct device *dev, void *res) +{ +	clkdev_drop(*(struct clk_lookup **)res); +} + +static int devm_clk_match_clkdev(struct device *dev, void *res, void *data) +{ +	struct clk_lookup **l = res; -	return cl ? 0 : -ENOMEM; +	return *l == data;  } -EXPORT_SYMBOL(clk_hw_register_clkdev); + +/** + * devm_clk_release_clkdev - Resource managed clkdev lookup release + * @dev: device this lookup is bound + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * Drop the clkdev lookup created with devm_clk_hw_register_clkdev. + * Normally this function will not need to be called and the resource + * management code will ensure that the resource is freed. + */ +void devm_clk_release_clkdev(struct device *dev, const char *con_id, +			     const char *dev_id) +{ +	struct clk_lookup *cl; +	int rval; + +	cl = clk_find(dev_id, con_id); +	WARN_ON(!cl); +	rval = devres_release(dev, devm_clkdev_release, +			      devm_clk_match_clkdev, cl); +	WARN_ON(rval); +} +EXPORT_SYMBOL(devm_clk_release_clkdev); + +/** + * devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw + * @dev: device this lookup is bound + * @hw: struct clk_hw to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * con_id or dev_id may be NULL as a wildcard, just as in the rest of + * clkdev. + * + * To make things easier for mass registration, we detect error clk_hws + * from a previous clk_hw_register_*() call, and return the error code for + * those.  This is to permit this function to be called immediately + * after clk_hw_register_*(). + */ +int devm_clk_hw_register_clkdev(struct device *dev, struct clk_hw *hw, +				const char *con_id, const char *dev_id) +{ +	int rval = -ENOMEM; +	struct clk_lookup **cl; + +	cl = devres_alloc(devm_clkdev_release, sizeof(*cl), GFP_KERNEL); +	if (cl) { +		rval = do_clk_register_clkdev(hw, cl, con_id, dev_id); +		if (!rval) +			devres_add(dev, cl); +		else +			devres_free(cl); +	} +	return rval; +} +EXPORT_SYMBOL(devm_clk_hw_register_clkdev); | 
