1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * OMAP gate 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 | |
17 | #include "clock.h" |
18 | |
19 | #undef pr_fmt |
20 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
21 | |
22 | static int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *clk); |
23 | |
24 | static const struct clk_ops omap_gate_clkdm_clk_ops = { |
25 | .init = &omap2_init_clk_clkdm, |
26 | .enable = &omap2_clkops_enable_clkdm, |
27 | .disable = &omap2_clkops_disable_clkdm, |
28 | .restore_context = clk_gate_restore_context, |
29 | }; |
30 | |
31 | const struct clk_ops omap_gate_clk_ops = { |
32 | .init = &omap2_init_clk_clkdm, |
33 | .enable = &omap2_dflt_clk_enable, |
34 | .disable = &omap2_dflt_clk_disable, |
35 | .is_enabled = &omap2_dflt_clk_is_enabled, |
36 | .restore_context = clk_gate_restore_context, |
37 | }; |
38 | |
39 | static const struct clk_ops omap_gate_clk_hsdiv_restore_ops = { |
40 | .init = &omap2_init_clk_clkdm, |
41 | .enable = &omap36xx_gate_clk_enable_with_hsdiv_restore, |
42 | .disable = &omap2_dflt_clk_disable, |
43 | .is_enabled = &omap2_dflt_clk_is_enabled, |
44 | .restore_context = clk_gate_restore_context, |
45 | }; |
46 | |
47 | /** |
48 | * omap36xx_gate_clk_enable_with_hsdiv_restore - enable clocks suffering |
49 | * from HSDivider PWRDN problem Implements Errata ID: i556. |
50 | * @hw: DPLL output struct clk_hw |
51 | * |
52 | * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck, |
53 | * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset |
54 | * valueafter their respective PWRDN bits are set. Any dummy write |
55 | * (Any other value different from the Read value) to the |
56 | * corresponding CM_CLKSEL register will refresh the dividers. |
57 | */ |
58 | static int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *hw) |
59 | { |
60 | struct clk_omap_divider *parent; |
61 | struct clk_hw *parent_hw; |
62 | u32 dummy_v, orig_v; |
63 | int ret; |
64 | |
65 | /* Clear PWRDN bit of HSDIVIDER */ |
66 | ret = omap2_dflt_clk_enable(hw); |
67 | |
68 | /* Parent is the x2 node, get parent of parent for the m2 div */ |
69 | parent_hw = clk_hw_get_parent(hw: clk_hw_get_parent(hw)); |
70 | parent = to_clk_omap_divider(parent_hw); |
71 | |
72 | /* Restore the dividers */ |
73 | if (!ret) { |
74 | orig_v = ti_clk_ll_ops->clk_readl(&parent->reg); |
75 | dummy_v = orig_v; |
76 | |
77 | /* Write any other value different from the Read value */ |
78 | dummy_v ^= (1 << parent->shift); |
79 | ti_clk_ll_ops->clk_writel(dummy_v, &parent->reg); |
80 | |
81 | /* Write the original divider */ |
82 | ti_clk_ll_ops->clk_writel(orig_v, &parent->reg); |
83 | } |
84 | |
85 | return ret; |
86 | } |
87 | |
88 | static struct clk *_register_gate(struct device_node *node, const char *name, |
89 | const char *parent_name, unsigned long flags, |
90 | struct clk_omap_reg *reg, u8 bit_idx, |
91 | u8 clk_gate_flags, const struct clk_ops *ops, |
92 | const struct clk_hw_omap_ops *hw_ops) |
93 | { |
94 | struct clk_init_data init = { NULL }; |
95 | struct clk_hw_omap *clk_hw; |
96 | struct clk *clk; |
97 | |
98 | clk_hw = kzalloc(size: sizeof(*clk_hw), GFP_KERNEL); |
99 | if (!clk_hw) |
100 | return ERR_PTR(error: -ENOMEM); |
101 | |
102 | clk_hw->hw.init = &init; |
103 | |
104 | init.name = name; |
105 | init.ops = ops; |
106 | |
107 | memcpy(&clk_hw->enable_reg, reg, sizeof(*reg)); |
108 | clk_hw->enable_bit = bit_idx; |
109 | clk_hw->ops = hw_ops; |
110 | |
111 | clk_hw->flags = clk_gate_flags; |
112 | |
113 | init.parent_names = &parent_name; |
114 | init.num_parents = 1; |
115 | |
116 | init.flags = flags; |
117 | |
118 | clk = of_ti_clk_register_omap_hw(node, hw: &clk_hw->hw, con: name); |
119 | |
120 | if (IS_ERR(ptr: clk)) |
121 | kfree(objp: clk_hw); |
122 | |
123 | return clk; |
124 | } |
125 | |
126 | static void __init _of_ti_gate_clk_setup(struct device_node *node, |
127 | const struct clk_ops *ops, |
128 | const struct clk_hw_omap_ops *hw_ops) |
129 | { |
130 | struct clk *clk; |
131 | const char *parent_name; |
132 | struct clk_omap_reg reg; |
133 | const char *name; |
134 | u8 enable_bit = 0; |
135 | u32 val; |
136 | u32 flags = 0; |
137 | u8 clk_gate_flags = 0; |
138 | |
139 | if (ops != &omap_gate_clkdm_clk_ops) { |
140 | if (ti_clk_get_reg_addr(node, index: 0, reg: ®)) |
141 | return; |
142 | |
143 | if (!of_property_read_u32(np: node, propname: "ti,bit-shift" , out_value: &val)) |
144 | enable_bit = val; |
145 | } |
146 | |
147 | if (of_clk_get_parent_count(np: node) != 1) { |
148 | pr_err("%pOFn must have 1 parent\n" , node); |
149 | return; |
150 | } |
151 | |
152 | parent_name = of_clk_get_parent_name(np: node, index: 0); |
153 | |
154 | if (of_property_read_bool(np: node, propname: "ti,set-rate-parent" )) |
155 | flags |= CLK_SET_RATE_PARENT; |
156 | |
157 | if (of_property_read_bool(np: node, propname: "ti,set-bit-to-disable" )) |
158 | clk_gate_flags |= INVERT_ENABLE; |
159 | |
160 | name = ti_dt_clk_name(np: node); |
161 | clk = _register_gate(node, name, parent_name, flags, reg: ®, |
162 | bit_idx: enable_bit, clk_gate_flags, ops, hw_ops); |
163 | |
164 | if (!IS_ERR(ptr: clk)) |
165 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_simple_get, data: clk); |
166 | } |
167 | |
168 | static void __init |
169 | _of_ti_composite_gate_clk_setup(struct device_node *node, |
170 | const struct clk_hw_omap_ops *hw_ops) |
171 | { |
172 | struct clk_hw_omap *gate; |
173 | u32 val = 0; |
174 | |
175 | gate = kzalloc(size: sizeof(*gate), GFP_KERNEL); |
176 | if (!gate) |
177 | return; |
178 | |
179 | if (ti_clk_get_reg_addr(node, index: 0, reg: &gate->enable_reg)) |
180 | goto cleanup; |
181 | |
182 | of_property_read_u32(np: node, propname: "ti,bit-shift" , out_value: &val); |
183 | |
184 | gate->enable_bit = val; |
185 | gate->ops = hw_ops; |
186 | |
187 | if (!ti_clk_add_component(node, hw: &gate->hw, type: CLK_COMPONENT_TYPE_GATE)) |
188 | return; |
189 | |
190 | cleanup: |
191 | kfree(objp: gate); |
192 | } |
193 | |
194 | static void __init |
195 | of_ti_composite_no_wait_gate_clk_setup(struct device_node *node) |
196 | { |
197 | _of_ti_composite_gate_clk_setup(node, NULL); |
198 | } |
199 | CLK_OF_DECLARE(ti_composite_no_wait_gate_clk, "ti,composite-no-wait-gate-clock" , |
200 | of_ti_composite_no_wait_gate_clk_setup); |
201 | |
202 | #if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) |
203 | static void __init of_ti_composite_interface_clk_setup(struct device_node *node) |
204 | { |
205 | _of_ti_composite_gate_clk_setup(node, &clkhwops_iclk_wait); |
206 | } |
207 | CLK_OF_DECLARE(ti_composite_interface_clk, "ti,composite-interface-clock" , |
208 | of_ti_composite_interface_clk_setup); |
209 | #endif |
210 | |
211 | static void __init of_ti_composite_gate_clk_setup(struct device_node *node) |
212 | { |
213 | _of_ti_composite_gate_clk_setup(node, hw_ops: &clkhwops_wait); |
214 | } |
215 | CLK_OF_DECLARE(ti_composite_gate_clk, "ti,composite-gate-clock" , |
216 | of_ti_composite_gate_clk_setup); |
217 | |
218 | |
219 | static void __init of_ti_clkdm_gate_clk_setup(struct device_node *node) |
220 | { |
221 | _of_ti_gate_clk_setup(node, ops: &omap_gate_clkdm_clk_ops, NULL); |
222 | } |
223 | CLK_OF_DECLARE(ti_clkdm_gate_clk, "ti,clkdm-gate-clock" , |
224 | of_ti_clkdm_gate_clk_setup); |
225 | |
226 | static void __init of_ti_hsdiv_gate_clk_setup(struct device_node *node) |
227 | { |
228 | _of_ti_gate_clk_setup(node, ops: &omap_gate_clk_hsdiv_restore_ops, |
229 | hw_ops: &clkhwops_wait); |
230 | } |
231 | CLK_OF_DECLARE(ti_hsdiv_gate_clk, "ti,hsdiv-gate-clock" , |
232 | of_ti_hsdiv_gate_clk_setup); |
233 | |
234 | static void __init of_ti_gate_clk_setup(struct device_node *node) |
235 | { |
236 | _of_ti_gate_clk_setup(node, ops: &omap_gate_clk_ops, NULL); |
237 | } |
238 | CLK_OF_DECLARE(ti_gate_clk, "ti,gate-clock" , of_ti_gate_clk_setup); |
239 | |
240 | static void __init of_ti_wait_gate_clk_setup(struct device_node *node) |
241 | { |
242 | _of_ti_gate_clk_setup(node, ops: &omap_gate_clk_ops, hw_ops: &clkhwops_wait); |
243 | } |
244 | CLK_OF_DECLARE(ti_wait_gate_clk, "ti,wait-gate-clock" , |
245 | of_ti_wait_gate_clk_setup); |
246 | |
247 | #ifdef CONFIG_ARCH_OMAP3 |
248 | static void __init of_ti_am35xx_gate_clk_setup(struct device_node *node) |
249 | { |
250 | _of_ti_gate_clk_setup(node, &omap_gate_clk_ops, |
251 | &clkhwops_am35xx_ipss_module_wait); |
252 | } |
253 | CLK_OF_DECLARE(ti_am35xx_gate_clk, "ti,am35xx-gate-clock" , |
254 | of_ti_am35xx_gate_clk_setup); |
255 | |
256 | static void __init of_ti_dss_gate_clk_setup(struct device_node *node) |
257 | { |
258 | _of_ti_gate_clk_setup(node, &omap_gate_clk_ops, |
259 | &clkhwops_omap3430es2_dss_usbhost_wait); |
260 | } |
261 | CLK_OF_DECLARE(ti_dss_gate_clk, "ti,dss-gate-clock" , |
262 | of_ti_dss_gate_clk_setup); |
263 | #endif |
264 | |