1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021 Semihalf.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/bus.h>
30 #include <sys/kernel.h>
31 #include <sys/module.h>
32 #include <sys/mutex.h>
33 #include <sys/rman.h>
34 #include <machine/bus.h>
35
36 #include <dev/fdt/simplebus.h>
37
38 #include <dev/clk/clk.h>
39 #include <dev/clk/clk_div.h>
40 #include <dev/clk/clk_fixed.h>
41 #include <dev/clk/clk_gate.h>
42 #include <dev/clk/clk_mux.h>
43
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46
47 #include "clkdev_if.h"
48 #include "periph.h"
49
50 #define PARENT_CNT 2
51
52 /*
53 * Register chain: mux (select proper TBG) -> div1 (first frequency divider) ->
54 * div2 (second frequency divider) -> mux (select divided freq.
55 * or xtal output) -> gate (enable or disable clock), which is also final node
56 */
57
58 int
a37x0_periph_d_register_full_clk_dd(struct clkdom * clkdom,struct a37x0_periph_clknode_def * device_def)59 a37x0_periph_d_register_full_clk_dd(struct clkdom *clkdom,
60 struct a37x0_periph_clknode_def *device_def)
61 {
62 const char *parent_names[PARENT_CNT];
63 struct clk_mux_def *clk_mux;
64 struct clk_mux_def *tbg_mux;
65 struct clk_gate_def *gate;
66 struct clk_div_def *div1;
67 struct clk_div_def *div2;
68 int error, dev_id;
69
70 dev_id = device_def->common_def.device_id;
71 tbg_mux = &device_def->clk_def.full_dd.tbg_mux;
72 div1 = &device_def->clk_def.full_dd.div1;
73 div2 = &device_def->clk_def.full_dd.div2;
74 gate = &device_def->clk_def.full_dd.gate;
75 clk_mux = &device_def->clk_def.full_dd.clk_mux;
76
77 a37x0_periph_set_props(&tbg_mux->clkdef, device_def->common_def.tbgs,
78 device_def->common_def.tbg_cnt);
79
80 error = a37x0_periph_create_mux(clkdom,
81 tbg_mux, A37x0_INTERNAL_CLK_ID(dev_id, MUX_POS));
82 if (error)
83 goto fail;
84
85 a37x0_periph_set_props(&div1->clkdef, &tbg_mux->clkdef.name, 1);
86 error = a37x0_periph_create_div(clkdom, div1,
87 A37x0_INTERNAL_CLK_ID(dev_id, DIV1_POS));
88 if (error)
89 goto fail;
90
91 a37x0_periph_set_props(&div2->clkdef, &div1->clkdef.name, 1);
92 error = a37x0_periph_create_div(clkdom, div2,
93 A37x0_INTERNAL_CLK_ID(dev_id, DIV2_POS));
94 if (error)
95 goto fail;
96
97 parent_names[0] = device_def->common_def.xtal;
98 parent_names[1] = div2->clkdef.name;
99
100 a37x0_periph_set_props(&clk_mux->clkdef, parent_names, PARENT_CNT);
101 error = a37x0_periph_create_mux(clkdom, clk_mux,
102 A37x0_INTERNAL_CLK_ID(dev_id, CLK_MUX_POS));
103 if (error)
104 goto fail;
105
106 a37x0_periph_set_props(&gate->clkdef, &clk_mux->clkdef.name, 1);
107 error = a37x0_periph_create_gate(clkdom, gate,
108 dev_id);
109 if (error)
110 goto fail;
111
112 fail:
113
114 return (error);
115 }
116
117 /*
118 * Register chain: mux (select proper TBG) -> div1 (first frequency divider) ->
119 * mux (select divided freq. or xtal output) -> gate (enable or disable clock),
120 * which is also final node
121 */
122
123 int
a37x0_periph_d_register_full_clk(struct clkdom * clkdom,struct a37x0_periph_clknode_def * device_def)124 a37x0_periph_d_register_full_clk(struct clkdom *clkdom,
125 struct a37x0_periph_clknode_def *device_def)
126 {
127 const char *parent_names[PARENT_CNT];
128 struct clk_mux_def *tbg_mux;
129 struct clk_mux_def *clk_mux;
130 struct clk_gate_def *gate;
131 struct clk_div_def *div;
132 int error, dev_id;
133
134 dev_id = device_def->common_def.device_id;
135 tbg_mux = &device_def->clk_def.full_d.tbg_mux;
136 div = &device_def->clk_def.full_d.div;
137 gate = &device_def->clk_def.full_d.gate;
138 clk_mux = &device_def->clk_def.full_d. clk_mux;
139
140 a37x0_periph_set_props(&tbg_mux->clkdef, device_def->common_def.tbgs,
141 device_def->common_def.tbg_cnt);
142 error = a37x0_periph_create_mux(clkdom, tbg_mux,
143 A37x0_INTERNAL_CLK_ID(device_def->common_def.device_id, MUX_POS));
144 if (error)
145 goto fail;
146
147 a37x0_periph_set_props(&div->clkdef, &tbg_mux->clkdef.name, 1);
148 error = a37x0_periph_create_div(clkdom, div,
149 A37x0_INTERNAL_CLK_ID(device_def->common_def.device_id, DIV1_POS));
150 if (error)
151 goto fail;
152
153 parent_names[0] = device_def->common_def.xtal;
154 parent_names[1] = div->clkdef.name;
155
156 a37x0_periph_set_props(&clk_mux->clkdef, parent_names, PARENT_CNT);
157 error = a37x0_periph_create_mux(clkdom, clk_mux,
158 A37x0_INTERNAL_CLK_ID(dev_id, CLK_MUX_POS));
159 if (error)
160 goto fail;
161
162 a37x0_periph_set_props(&gate->clkdef, &clk_mux->clkdef.name, 1);
163 error = a37x0_periph_create_gate(clkdom, gate,
164 dev_id);
165 if (error)
166 goto fail;
167
168 fail:
169
170 return (error);
171 }
172
173 /*
174 * Register CPU clock. It consists of mux (select proper TBG) -> div (frequency
175 * divider) -> mux (choose divided or xtal output).
176 */
177
178 int
a37x0_periph_d_register_periph_cpu(struct clkdom * clkdom,struct a37x0_periph_clknode_def * device_def)179 a37x0_periph_d_register_periph_cpu(struct clkdom *clkdom,
180 struct a37x0_periph_clknode_def *device_def)
181 {
182 const char *parent_names[PARENT_CNT];
183 struct clk_mux_def *clk_mux;
184 struct clk_mux_def *tbg_mux;
185 struct clk_div_def *div;
186 int error, dev_id;
187
188 dev_id = device_def->common_def.device_id;
189 tbg_mux = &device_def->clk_def.cpu.tbg_mux;
190 div = &device_def->clk_def.cpu.div;
191 clk_mux = &device_def->clk_def.cpu.clk_mux;
192
193 a37x0_periph_set_props(&tbg_mux->clkdef, device_def->common_def.tbgs,
194 device_def->common_def.tbg_cnt);
195 error = a37x0_periph_create_mux(clkdom, tbg_mux,
196 A37x0_INTERNAL_CLK_ID(dev_id, MUX_POS));
197 if (error)
198 goto fail;
199
200 a37x0_periph_set_props(&div->clkdef, &tbg_mux->clkdef.name, 1);
201 error = a37x0_periph_create_div(clkdom, div,
202 A37x0_INTERNAL_CLK_ID(dev_id, DIV1_POS));
203 if (error)
204 goto fail;
205
206 parent_names[0] = device_def->common_def.xtal;
207 parent_names[1] = div->clkdef.name;
208
209 a37x0_periph_set_props(&clk_mux->clkdef, parent_names, PARENT_CNT);
210 error = a37x0_periph_create_mux(clkdom, clk_mux,
211 dev_id);
212
213 fail:
214
215 return (error);
216 }
217
218 /*
219 * Register chain: mux (choose proper TBG) -> div1 (first frequency divider) ->
220 * div2 (second frequency divider) -> mux (choose divided or xtal output).
221 */
222 int
a37x0_periph_d_register_mdd(struct clkdom * clkdom,struct a37x0_periph_clknode_def * device_def)223 a37x0_periph_d_register_mdd(struct clkdom *clkdom,
224 struct a37x0_periph_clknode_def *device_def)
225 {
226 const char *parent_names[PARENT_CNT];
227 struct clk_mux_def *tbg_mux;
228 struct clk_mux_def *clk_mux;
229 struct clk_div_def *div1;
230 struct clk_div_def *div2;
231 int error, dev_id;
232
233 dev_id = device_def->common_def.device_id;
234 tbg_mux = &device_def->clk_def.mdd.tbg_mux;
235 div1 = &device_def->clk_def.mdd.div1;
236 div2 = &device_def->clk_def.mdd.div2;
237 clk_mux = &device_def->clk_def.mdd.clk_mux;
238
239 a37x0_periph_set_props(&tbg_mux->clkdef, device_def->common_def.tbgs,
240 device_def->common_def.tbg_cnt);
241 error = a37x0_periph_create_mux(clkdom, tbg_mux,
242 A37x0_INTERNAL_CLK_ID(dev_id, MUX_POS));
243 if (error)
244 goto fail;
245
246 a37x0_periph_set_props(&div1->clkdef, &tbg_mux->clkdef.name, 1);
247 error = a37x0_periph_create_div(clkdom, div1,
248 A37x0_INTERNAL_CLK_ID(dev_id, DIV1_POS));
249 if (error)
250 goto fail;
251
252 a37x0_periph_set_props(&div2->clkdef, &div1->clkdef.name, 1);
253 error = a37x0_periph_create_div(clkdom, div2,
254 A37x0_INTERNAL_CLK_ID(dev_id, DIV2_POS));
255
256 if (error)
257 goto fail;
258
259 parent_names[0] = device_def->common_def.xtal;
260 parent_names[1] = div2->clkdef.name;
261
262 a37x0_periph_set_props(&clk_mux->clkdef, parent_names, PARENT_CNT);
263 error = a37x0_periph_create_mux(clkdom, clk_mux,
264 dev_id);
265 if (error)
266 goto fail;
267
268 fail:
269
270 return (error);
271 }
272