1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * TI composite clock support |
4 | * |
5 | * Copyright (C) 2013 Texas Instruments, Inc. |
6 | * |
7 | * Tero Kristo <t-kristo@ti.com> |
8 | */ |
9 | |
10 | #include <linux/clk-provider.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/io.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_address.h> |
15 | #include <linux/clk/ti.h> |
16 | #include <linux/list.h> |
17 | |
18 | #include "clock.h" |
19 | |
20 | #undef pr_fmt |
21 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
22 | |
23 | static unsigned long ti_composite_recalc_rate(struct clk_hw *hw, |
24 | unsigned long parent_rate) |
25 | { |
26 | return ti_clk_divider_ops.recalc_rate(hw, parent_rate); |
27 | } |
28 | |
29 | static long ti_composite_round_rate(struct clk_hw *hw, unsigned long rate, |
30 | unsigned long *prate) |
31 | { |
32 | return -EINVAL; |
33 | } |
34 | |
35 | static int ti_composite_set_rate(struct clk_hw *hw, unsigned long rate, |
36 | unsigned long parent_rate) |
37 | { |
38 | return -EINVAL; |
39 | } |
40 | |
41 | static const struct clk_ops ti_composite_divider_ops = { |
42 | .recalc_rate = &ti_composite_recalc_rate, |
43 | .round_rate = &ti_composite_round_rate, |
44 | .set_rate = &ti_composite_set_rate, |
45 | }; |
46 | |
47 | static const struct clk_ops ti_composite_gate_ops = { |
48 | .enable = &omap2_dflt_clk_enable, |
49 | .disable = &omap2_dflt_clk_disable, |
50 | .is_enabled = &omap2_dflt_clk_is_enabled, |
51 | }; |
52 | |
53 | struct component_clk { |
54 | int num_parents; |
55 | const char **parent_names; |
56 | struct device_node *node; |
57 | int type; |
58 | struct clk_hw *hw; |
59 | struct list_head link; |
60 | }; |
61 | |
62 | static const char * const component_clk_types[] __initconst = { |
63 | "gate" , "divider" , "mux" |
64 | }; |
65 | |
66 | static LIST_HEAD(component_clks); |
67 | |
68 | static struct device_node *_get_component_node(struct device_node *node, int i) |
69 | { |
70 | int rc; |
71 | struct of_phandle_args clkspec; |
72 | |
73 | rc = of_parse_phandle_with_args(np: node, list_name: "clocks" , cells_name: "#clock-cells" , index: i, |
74 | out_args: &clkspec); |
75 | if (rc) |
76 | return NULL; |
77 | |
78 | return clkspec.np; |
79 | } |
80 | |
81 | static struct component_clk *_lookup_component(struct device_node *node) |
82 | { |
83 | struct component_clk *comp; |
84 | |
85 | list_for_each_entry(comp, &component_clks, link) { |
86 | if (comp->node == node) |
87 | return comp; |
88 | } |
89 | return NULL; |
90 | } |
91 | |
92 | struct clk_hw_omap_comp { |
93 | struct clk_hw hw; |
94 | struct device_node *comp_nodes[CLK_COMPONENT_TYPE_MAX]; |
95 | struct component_clk *comp_clks[CLK_COMPONENT_TYPE_MAX]; |
96 | }; |
97 | |
98 | static inline struct clk_hw *_get_hw(struct clk_hw_omap_comp *clk, int idx) |
99 | { |
100 | if (!clk) |
101 | return NULL; |
102 | |
103 | if (!clk->comp_clks[idx]) |
104 | return NULL; |
105 | |
106 | return clk->comp_clks[idx]->hw; |
107 | } |
108 | |
109 | #define to_clk_hw_comp(_hw) container_of(_hw, struct clk_hw_omap_comp, hw) |
110 | |
111 | static void __init _register_composite(void *user, |
112 | struct device_node *node) |
113 | { |
114 | struct clk_hw *hw = user; |
115 | struct clk *clk; |
116 | struct clk_hw_omap_comp *cclk = to_clk_hw_comp(hw); |
117 | struct component_clk *comp; |
118 | int num_parents = 0; |
119 | const char **parent_names = NULL; |
120 | const char *name; |
121 | int i; |
122 | int ret; |
123 | |
124 | /* Check for presence of each component clock */ |
125 | for (i = 0; i < CLK_COMPONENT_TYPE_MAX; i++) { |
126 | if (!cclk->comp_nodes[i]) |
127 | continue; |
128 | |
129 | comp = _lookup_component(node: cclk->comp_nodes[i]); |
130 | if (!comp) { |
131 | pr_debug("component %s not ready for %pOFn, retry\n" , |
132 | cclk->comp_nodes[i]->name, node); |
133 | if (!ti_clk_retry_init(node, user: hw, |
134 | func: _register_composite)) |
135 | return; |
136 | |
137 | goto cleanup; |
138 | } |
139 | if (cclk->comp_clks[comp->type] != NULL) { |
140 | pr_err("duplicate component types for %pOFn (%s)!\n" , |
141 | node, component_clk_types[comp->type]); |
142 | goto cleanup; |
143 | } |
144 | |
145 | cclk->comp_clks[comp->type] = comp; |
146 | |
147 | /* Mark this node as found */ |
148 | cclk->comp_nodes[i] = NULL; |
149 | } |
150 | |
151 | /* All components exists, proceed with registration */ |
152 | for (i = CLK_COMPONENT_TYPE_MAX - 1; i >= 0; i--) { |
153 | comp = cclk->comp_clks[i]; |
154 | if (!comp) |
155 | continue; |
156 | if (comp->num_parents) { |
157 | num_parents = comp->num_parents; |
158 | parent_names = comp->parent_names; |
159 | break; |
160 | } |
161 | } |
162 | |
163 | if (!num_parents) { |
164 | pr_err("%s: no parents found for %pOFn!\n" , __func__, node); |
165 | goto cleanup; |
166 | } |
167 | |
168 | name = ti_dt_clk_name(np: node); |
169 | clk = clk_register_composite(NULL, name, |
170 | parent_names, num_parents, |
171 | mux_hw: _get_hw(clk: cclk, idx: CLK_COMPONENT_TYPE_MUX), |
172 | mux_ops: &ti_clk_mux_ops, |
173 | rate_hw: _get_hw(clk: cclk, idx: CLK_COMPONENT_TYPE_DIVIDER), |
174 | rate_ops: &ti_composite_divider_ops, |
175 | gate_hw: _get_hw(clk: cclk, idx: CLK_COMPONENT_TYPE_GATE), |
176 | gate_ops: &ti_composite_gate_ops, flags: 0); |
177 | |
178 | if (!IS_ERR(ptr: clk)) { |
179 | ret = ti_clk_add_alias(clk, con: name); |
180 | if (ret) { |
181 | clk_unregister(clk); |
182 | goto cleanup; |
183 | } |
184 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_simple_get, data: clk); |
185 | } |
186 | |
187 | cleanup: |
188 | /* Free component clock list entries */ |
189 | for (i = 0; i < CLK_COMPONENT_TYPE_MAX; i++) { |
190 | if (!cclk->comp_clks[i]) |
191 | continue; |
192 | list_del(entry: &cclk->comp_clks[i]->link); |
193 | kfree(objp: cclk->comp_clks[i]->parent_names); |
194 | kfree(objp: cclk->comp_clks[i]); |
195 | } |
196 | |
197 | kfree(objp: cclk); |
198 | } |
199 | |
200 | static void __init of_ti_composite_clk_setup(struct device_node *node) |
201 | { |
202 | unsigned int num_clks; |
203 | int i; |
204 | struct clk_hw_omap_comp *cclk; |
205 | |
206 | /* Number of component clocks to be put inside this clock */ |
207 | num_clks = of_clk_get_parent_count(np: node); |
208 | |
209 | if (!num_clks) { |
210 | pr_err("composite clk %pOFn must have component(s)\n" , node); |
211 | return; |
212 | } |
213 | |
214 | cclk = kzalloc(size: sizeof(*cclk), GFP_KERNEL); |
215 | if (!cclk) |
216 | return; |
217 | |
218 | /* Get device node pointers for each component clock */ |
219 | for (i = 0; i < num_clks; i++) |
220 | cclk->comp_nodes[i] = _get_component_node(node, i); |
221 | |
222 | _register_composite(user: &cclk->hw, node); |
223 | } |
224 | CLK_OF_DECLARE(ti_composite_clock, "ti,composite-clock" , |
225 | of_ti_composite_clk_setup); |
226 | |
227 | /** |
228 | * ti_clk_add_component - add a component clock to the pool |
229 | * @node: device node of the component clock |
230 | * @hw: hardware clock definition for the component clock |
231 | * @type: type of the component clock |
232 | * |
233 | * Adds a component clock to the list of available components, so that |
234 | * it can be registered by a composite clock. |
235 | */ |
236 | int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw, |
237 | int type) |
238 | { |
239 | unsigned int num_parents; |
240 | const char **parent_names; |
241 | struct component_clk *clk; |
242 | |
243 | num_parents = of_clk_get_parent_count(np: node); |
244 | |
245 | if (!num_parents) { |
246 | pr_err("component-clock %pOFn must have parent(s)\n" , node); |
247 | return -EINVAL; |
248 | } |
249 | |
250 | parent_names = kcalloc(n: num_parents, size: sizeof(char *), GFP_KERNEL); |
251 | if (!parent_names) |
252 | return -ENOMEM; |
253 | |
254 | of_clk_parent_fill(np: node, parents: parent_names, size: num_parents); |
255 | |
256 | clk = kzalloc(size: sizeof(*clk), GFP_KERNEL); |
257 | if (!clk) { |
258 | kfree(objp: parent_names); |
259 | return -ENOMEM; |
260 | } |
261 | |
262 | clk->num_parents = num_parents; |
263 | clk->parent_names = parent_names; |
264 | clk->hw = hw; |
265 | clk->node = node; |
266 | clk->type = type; |
267 | list_add(new: &clk->link, head: &component_clks); |
268 | |
269 | return 0; |
270 | } |
271 | |