1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * OMAP clockdomain support
4   *
5   * Copyright (C) 2013 Texas Instruments, Inc.
6   *
7   * Tero Kristo <t-kristo@ti.com>
8   */
9  
10  #include <linux/clk.h>
11  #include <linux/clk-provider.h>
12  #include <linux/slab.h>
13  #include <linux/of.h>
14  #include <linux/of_address.h>
15  #include <linux/clk/ti.h>
16  
17  #include "clock.h"
18  
19  #undef pr_fmt
20  #define pr_fmt(fmt) "%s: " fmt, __func__
21  
22  /**
23   * omap2_clkops_enable_clkdm - increment usecount on clkdm of @hw
24   * @hw: struct clk_hw * of the clock being enabled
25   *
26   * Increment the usecount of the clockdomain of the clock pointed to
27   * by @hw; if the usecount is 1, the clockdomain will be "enabled."
28   * Only needed for clocks that don't use omap2_dflt_clk_enable() as
29   * their enable function pointer.  Passes along the return value of
30   * clkdm_clk_enable(), -EINVAL if @hw is not associated with a
31   * clockdomain, or 0 if clock framework-based clockdomain control is
32   * not implemented.
33   */
omap2_clkops_enable_clkdm(struct clk_hw * hw)34  int omap2_clkops_enable_clkdm(struct clk_hw *hw)
35  {
36  	struct clk_hw_omap *clk;
37  	int ret = 0;
38  
39  	clk = to_clk_hw_omap(hw);
40  
41  	if (unlikely(!clk->clkdm)) {
42  		pr_err("%s: %s: no clkdm set ?!\n", __func__,
43  		       clk_hw_get_name(hw));
44  		return -EINVAL;
45  	}
46  
47  	if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) {
48  		pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
49  		       __func__, clk_hw_get_name(hw));
50  		return 0;
51  	}
52  
53  	ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk);
54  	WARN(ret, "%s: could not enable %s's clockdomain %s: %d\n",
55  	     __func__, clk_hw_get_name(hw), clk->clkdm_name, ret);
56  
57  	return ret;
58  }
59  
60  /**
61   * omap2_clkops_disable_clkdm - decrement usecount on clkdm of @hw
62   * @hw: struct clk_hw * of the clock being disabled
63   *
64   * Decrement the usecount of the clockdomain of the clock pointed to
65   * by @hw; if the usecount is 0, the clockdomain will be "disabled."
66   * Only needed for clocks that don't use omap2_dflt_clk_disable() as their
67   * disable function pointer.  No return value.
68   */
omap2_clkops_disable_clkdm(struct clk_hw * hw)69  void omap2_clkops_disable_clkdm(struct clk_hw *hw)
70  {
71  	struct clk_hw_omap *clk;
72  
73  	clk = to_clk_hw_omap(hw);
74  
75  	if (unlikely(!clk->clkdm)) {
76  		pr_err("%s: %s: no clkdm set ?!\n", __func__,
77  		       clk_hw_get_name(hw));
78  		return;
79  	}
80  
81  	if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) {
82  		pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
83  		       __func__, clk_hw_get_name(hw));
84  		return;
85  	}
86  
87  	ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk);
88  }
89  
90  /**
91   * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
92   * @hw: Pointer to clk_hw_omap used to obtain OMAP clock struct ptr to use
93   *
94   * Convert a clockdomain name stored in a struct clk 'clk' into a
95   * clockdomain pointer, and save it into the struct clk.  Intended to be
96   * called during clk_register(). Returns 0 on success, -EERROR otherwise.
97   */
omap2_init_clk_clkdm(struct clk_hw * hw)98  int omap2_init_clk_clkdm(struct clk_hw *hw)
99  {
100  	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
101  	struct clockdomain *clkdm;
102  	const char *clk_name;
103  
104  	if (!clk->clkdm_name)
105  		return 0;
106  
107  	clk_name = __clk_get_name(hw->clk);
108  
109  	clkdm = ti_clk_ll_ops->clkdm_lookup(clk->clkdm_name);
110  	if (clkdm) {
111  		pr_debug("clock: associated clk %s to clkdm %s\n",
112  			 clk_name, clk->clkdm_name);
113  		clk->clkdm = clkdm;
114  	} else {
115  		pr_debug("clock: could not associate clk %s to clkdm %s\n",
116  			 clk_name, clk->clkdm_name);
117  	}
118  
119  	return 0;
120  }
121  
of_ti_clockdomain_setup(struct device_node * node)122  static void __init of_ti_clockdomain_setup(struct device_node *node)
123  {
124  	struct clk *clk;
125  	struct clk_hw *clk_hw;
126  	const char *clkdm_name = ti_dt_clk_name(node);
127  	int i;
128  	unsigned int num_clks;
129  
130  	num_clks = of_clk_get_parent_count(node);
131  
132  	for (i = 0; i < num_clks; i++) {
133  		clk = of_clk_get(node, i);
134  		if (IS_ERR(clk)) {
135  			pr_err("%s: Failed get %pOF' clock nr %d (%ld)\n",
136  			       __func__, node, i, PTR_ERR(clk));
137  			continue;
138  		}
139  		clk_hw = __clk_get_hw(clk);
140  		if (!omap2_clk_is_hw_omap(clk_hw)) {
141  			pr_warn("can't setup clkdm for basic clk %s\n",
142  				__clk_get_name(clk));
143  			clk_put(clk);
144  			continue;
145  		}
146  		to_clk_hw_omap(clk_hw)->clkdm_name = clkdm_name;
147  		omap2_init_clk_clkdm(clk_hw);
148  		clk_put(clk);
149  	}
150  }
151  
152  static const struct of_device_id ti_clkdm_match_table[] __initconst = {
153  	{ .compatible = "ti,clockdomain" },
154  	{ }
155  };
156  
157  /**
158   * ti_dt_clockdomains_setup - setup device tree clockdomains
159   *
160   * Initializes clockdomain nodes for a SoC. This parses through all the
161   * nodes with compatible = "ti,clockdomain", and add the clockdomain
162   * info for all the clocks listed under these. This function shall be
163   * called after rest of the DT clock init has completed and all
164   * clock nodes have been registered.
165   */
ti_dt_clockdomains_setup(void)166  void __init ti_dt_clockdomains_setup(void)
167  {
168  	struct device_node *np;
169  	for_each_matching_node(np, ti_clkdm_match_table) {
170  		of_ti_clockdomain_setup(np);
171  	}
172  }
173