diff options
Diffstat (limited to 'drivers/clk/clk.c')
| -rw-r--r-- | drivers/clk/clk.c | 175 | 
1 files changed, 138 insertions, 37 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1c46babeb093..1c677d7f7f53 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -37,6 +37,12 @@ static HLIST_HEAD(clk_root_list);  static HLIST_HEAD(clk_orphan_list);  static LIST_HEAD(clk_notifier_list); +static struct hlist_head *all_lists[] = { +	&clk_root_list, +	&clk_orphan_list, +	NULL, +}; +  /***    private data structures    ***/  struct clk_parent_map { @@ -615,6 +621,8 @@ static void clk_core_get_boundaries(struct clk_core *core,  {  	struct clk *clk_user; +	lockdep_assert_held(&prepare_lock); +  	*min_rate = core->min_rate;  	*max_rate = core->max_rate; @@ -2460,7 +2468,7 @@ static int clk_core_set_parent_nolock(struct clk_core *core,  	if (core->parent == parent)  		return 0; -	/* verify ops for for multi-parent clks */ +	/* verify ops for multi-parent clks */  	if (core->num_parents > 1 && !core->ops->set_parent)  		return -EPERM; @@ -2510,6 +2518,12 @@ runtime_put:  	return ret;  } +int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *parent) +{ +	return clk_core_set_parent_nolock(hw->core, parent->core); +} +EXPORT_SYMBOL_GPL(clk_hw_set_parent); +  /**   * clk_set_parent - switch the parent of a mux clk   * @clk: the mux clk whose input we are switching @@ -2856,12 +2870,6 @@ static int inited = 0;  static DEFINE_MUTEX(clk_debug_lock);  static HLIST_HEAD(clk_debug_list); -static struct hlist_head *all_lists[] = { -	&clk_root_list, -	&clk_orphan_list, -	NULL, -}; -  static struct hlist_head *orphan_list[] = {  	&clk_orphan_list,  	NULL, @@ -2870,9 +2878,6 @@ static struct hlist_head *orphan_list[] = {  static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,  				 int level)  { -	if (!c) -		return; -  	seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %5d %6d\n",  		   level * 3 + 1, "",  		   30 - level * 3, c->name, @@ -2887,9 +2892,6 @@ static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,  {  	struct clk_core *child; -	if (!c) -		return; -  	clk_summary_show_one(s, c, level);  	hlist_for_each_entry(child, &c->children, child_node) @@ -2919,8 +2921,9 @@ DEFINE_SHOW_ATTRIBUTE(clk_summary);  static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)  { -	if (!c) -		return; +	unsigned long min_rate, max_rate; + +	clk_core_get_boundaries(c, &min_rate, &max_rate);  	/* This should be JSON format, i.e. elements separated with a comma */  	seq_printf(s, "\"%s\": { ", c->name); @@ -2928,6 +2931,8 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)  	seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);  	seq_printf(s, "\"protect_count\": %d,", c->protect_count);  	seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); +	seq_printf(s, "\"min_rate\": %lu,", min_rate); +	seq_printf(s, "\"max_rate\": %lu,", max_rate);  	seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c));  	seq_printf(s, "\"phase\": %d,", clk_core_get_phase(c));  	seq_printf(s, "\"duty_cycle\": %u", @@ -2938,9 +2943,6 @@ static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level)  {  	struct clk_core *child; -	if (!c) -		return; -  	clk_dump_one(s, c, level);  	hlist_for_each_entry(child, &c->children, child_node) { @@ -3036,15 +3038,15 @@ static void possible_parent_show(struct seq_file *s, struct clk_core *core,  	 */  	parent = clk_core_get_parent_by_index(core, i);  	if (parent) -		seq_printf(s, "%s", parent->name); +		seq_puts(s, parent->name);  	else if (core->parents[i].name) -		seq_printf(s, "%s", core->parents[i].name); +		seq_puts(s, core->parents[i].name);  	else if (core->parents[i].fw_name)  		seq_printf(s, "<%s>(fw)", core->parents[i].fw_name);  	else if (core->parents[i].index >= 0) -		seq_printf(s, "%s", -			   of_clk_get_parent_name(core->of_node, -						  core->parents[i].index)); +		seq_puts(s, +			 of_clk_get_parent_name(core->of_node, +						core->parents[i].index));  	else  		seq_puts(s, "(missing)"); @@ -3087,6 +3089,34 @@ static int clk_duty_cycle_show(struct seq_file *s, void *data)  }  DEFINE_SHOW_ATTRIBUTE(clk_duty_cycle); +static int clk_min_rate_show(struct seq_file *s, void *data) +{ +	struct clk_core *core = s->private; +	unsigned long min_rate, max_rate; + +	clk_prepare_lock(); +	clk_core_get_boundaries(core, &min_rate, &max_rate); +	clk_prepare_unlock(); +	seq_printf(s, "%lu\n", min_rate); + +	return 0; +} +DEFINE_SHOW_ATTRIBUTE(clk_min_rate); + +static int clk_max_rate_show(struct seq_file *s, void *data) +{ +	struct clk_core *core = s->private; +	unsigned long min_rate, max_rate; + +	clk_prepare_lock(); +	clk_core_get_boundaries(core, &min_rate, &max_rate); +	clk_prepare_unlock(); +	seq_printf(s, "%lu\n", max_rate); + +	return 0; +} +DEFINE_SHOW_ATTRIBUTE(clk_max_rate); +  static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)  {  	struct dentry *root; @@ -3098,6 +3128,8 @@ static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)  	core->dentry = root;  	debugfs_create_ulong("clk_rate", 0444, root, &core->rate); +	debugfs_create_file("clk_min_rate", 0444, root, core, &clk_min_rate_fops); +	debugfs_create_file("clk_max_rate", 0444, root, core, &clk_max_rate_fops);  	debugfs_create_ulong("clk_accuracy", 0444, root, &core->accuracy);  	debugfs_create_u32("clk_phase", 0444, root, &core->phase);  	debugfs_create_file("clk_flags", 0444, root, core, &clk_flags_fops); @@ -3507,9 +3539,9 @@ static int clk_cpy_name(const char **dst_p, const char *src, bool must_exist)  	return 0;  } -static int clk_core_populate_parent_map(struct clk_core *core) +static int clk_core_populate_parent_map(struct clk_core *core, +					const struct clk_init_data *init)  { -	const struct clk_init_data *init = core->hw->init;  	u8 num_parents = init->num_parents;  	const char * const *parent_names = init->parent_names;  	const struct clk_hw **parent_hws = init->parent_hws; @@ -3589,6 +3621,14 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)  {  	int ret;  	struct clk_core *core; +	const struct clk_init_data *init = hw->init; + +	/* +	 * The init data is not supposed to be used outside of registration path. +	 * Set it to NULL so that provider drivers can't use it either and so that +	 * we catch use of hw->init early on in the core. +	 */ +	hw->init = NULL;  	core = kzalloc(sizeof(*core), GFP_KERNEL);  	if (!core) { @@ -3596,17 +3636,17 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)  		goto fail_out;  	} -	core->name = kstrdup_const(hw->init->name, GFP_KERNEL); +	core->name = kstrdup_const(init->name, GFP_KERNEL);  	if (!core->name) {  		ret = -ENOMEM;  		goto fail_name;  	} -	if (WARN_ON(!hw->init->ops)) { +	if (WARN_ON(!init->ops)) {  		ret = -EINVAL;  		goto fail_ops;  	} -	core->ops = hw->init->ops; +	core->ops = init->ops;  	if (dev && pm_runtime_enabled(dev))  		core->rpm_enabled = true; @@ -3615,13 +3655,13 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)  	if (dev && dev->driver)  		core->owner = dev->driver->owner;  	core->hw = hw; -	core->flags = hw->init->flags; -	core->num_parents = hw->init->num_parents; +	core->flags = init->flags; +	core->num_parents = init->num_parents;  	core->min_rate = 0;  	core->max_rate = ULONG_MAX;  	hw->core = core; -	ret = clk_core_populate_parent_map(core); +	ret = clk_core_populate_parent_map(core, init);  	if (ret)  		goto fail_parents; @@ -3760,6 +3800,34 @@ static const struct clk_ops clk_nodrv_ops = {  	.set_parent	= clk_nodrv_set_parent,  }; +static void clk_core_evict_parent_cache_subtree(struct clk_core *root, +						struct clk_core *target) +{ +	int i; +	struct clk_core *child; + +	for (i = 0; i < root->num_parents; i++) +		if (root->parents[i].core == target) +			root->parents[i].core = NULL; + +	hlist_for_each_entry(child, &root->children, child_node) +		clk_core_evict_parent_cache_subtree(child, target); +} + +/* Remove this clk from all parent caches */ +static void clk_core_evict_parent_cache(struct clk_core *core) +{ +	struct hlist_head **lists; +	struct clk_core *root; + +	lockdep_assert_held(&prepare_lock); + +	for (lists = all_lists; *lists; lists++) +		hlist_for_each_entry(root, *lists, child_node) +			clk_core_evict_parent_cache_subtree(root, core); + +} +  /**   * clk_unregister - unregister a currently registered clock   * @clk: clock to unregister @@ -3798,6 +3866,8 @@ void clk_unregister(struct clk *clk)  			clk_core_set_parent_nolock(child, NULL);  	} +	clk_core_evict_parent_cache(clk->core); +  	hlist_del_init(&clk->core->child_node);  	if (clk->core->prepare_count) @@ -4339,12 +4409,43 @@ void devm_of_clk_del_provider(struct device *dev)  }  EXPORT_SYMBOL(devm_of_clk_del_provider); -/* - * Beware the return values when np is valid, but no clock provider is found. - * If name == NULL, the function returns -ENOENT. - * If name != NULL, the function returns -EINVAL. This is because - * of_parse_phandle_with_args() is called even if of_property_match_string() - * returns an error. +/** + * of_parse_clkspec() - Parse a DT clock specifier for a given device node + * @np: device node to parse clock specifier from + * @index: index of phandle to parse clock out of. If index < 0, @name is used + * @name: clock name to find and parse. If name is NULL, the index is used + * @out_args: Result of parsing the clock specifier + * + * Parses a device node's "clocks" and "clock-names" properties to find the + * phandle and cells for the index or name that is desired. The resulting clock + * specifier is placed into @out_args, or an errno is returned when there's a + * parsing error. The @index argument is ignored if @name is non-NULL. + * + * Example: + * + * phandle1: clock-controller@1 { + *	#clock-cells = <2>; + * } + * + * phandle2: clock-controller@2 { + *	#clock-cells = <1>; + * } + * + * clock-consumer@3 { + *	clocks = <&phandle1 1 2 &phandle2 3>; + *	clock-names = "name1", "name2"; + * } + * + * To get a device_node for `clock-controller@2' node you may call this + * function a few different ways: + * + *   of_parse_clkspec(clock-consumer@3, -1, "name2", &args); + *   of_parse_clkspec(clock-consumer@3, 1, NULL, &args); + *   of_parse_clkspec(clock-consumer@3, 1, "name2", &args); + * + * Return: 0 upon successfully parsing the clock specifier. Otherwise, -ENOENT + * if @name is NULL or -EINVAL if @name is non-NULL and it can't be found in + * the "clock-names" property of @np.   */  static int of_parse_clkspec(const struct device_node *np, int index,  			    const char *name, struct of_phandle_args *out_args)  |