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); |