1 /*-
2 * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/bus.h>
30 #include <sys/lock.h>
31 #include <sys/mutex.h>
32 #include <sys/rman.h>
33
34 #include <machine/bus.h>
35
36 #include <dev/clk/clk.h>
37
38 #include <dt-bindings/clock/tegra124-car.h>
39 #include "tegra124_car.h"
40
41 /* Flags */
42 #define SMF_HAVE_DIVIDER_2 1
43
44 struct super_mux_def {
45 struct clknode_init_def clkdef;
46 uint32_t base_reg;
47 uint32_t flags;
48 int src_pllx;
49 int src_div2;
50 };
51
52 #define PLIST(x) static const char *x[]
53 #define SM(_id, cn, pl, r, x, d, f) \
54 { \
55 .clkdef.id = _id, \
56 .clkdef.name = cn, \
57 .clkdef.parent_names = pl, \
58 .clkdef.parent_cnt = nitems(pl), \
59 .clkdef.flags = CLK_NODE_STATIC_STRINGS, \
60 .base_reg = r, \
61 .src_pllx = x, \
62 .src_div2 = d, \
63 .flags = f, \
64 }
65
66 PLIST(cclk_g_parents) = {
67 "clk_m", "pllC_out0", "clk_s", "pllM_out0",
68 "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
69 "pllX_out", NULL, NULL, NULL,
70 NULL, NULL, NULL,NULL, // "dfllCPU_out0"
71 };
72
73 PLIST(cclk_lp_parents) = {
74 "clk_m", "pllC_out0", "clk_s", "pllM_out0",
75 "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
76 "pllX_out", NULL, NULL, NULL,
77 NULL, NULL, NULL, NULL,
78 "pllX_out0"
79 };
80
81 PLIST(sclk_parents) = {
82 "clk_m", "pllC_out1", "pllP_out4", "pllP_out0",
83 "pllP_out2", "pllC_out0", "clk_s", "pllM_out1",
84 };
85
86 static struct super_mux_def super_mux_def[] = {
87 SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0),
88 SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2),
89 SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0),
90 };
91
92 static int super_mux_init(struct clknode *clk, device_t dev);
93 static int super_mux_set_mux(struct clknode *clk, int idx);
94
95 struct super_mux_sc {
96 device_t clkdev;
97 uint32_t base_reg;
98 int src_pllx;
99 int src_div2;
100 uint32_t flags;
101
102 int mux;
103 };
104
105 static clknode_method_t super_mux_methods[] = {
106 /* Device interface */
107 CLKNODEMETHOD(clknode_init, super_mux_init),
108 CLKNODEMETHOD(clknode_set_mux, super_mux_set_mux),
109 CLKNODEMETHOD_END
110 };
111 DEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods,
112 sizeof(struct super_mux_sc), clknode_class);
113
114 /* Mux status. */
115 #define SUPER_MUX_STATE_STDBY 0
116 #define SUPER_MUX_STATE_IDLE 1
117 #define SUPER_MUX_STATE_RUN 2
118 #define SUPER_MUX_STATE_IRQ 3
119 #define SUPER_MUX_STATE_FIQ 4
120
121 /* Mux register bits. */
122 #define SUPER_MUX_STATE_BIT_SHIFT 28
123 #define SUPER_MUX_STATE_BIT_MASK 0xF
124 /* State is Priority encoded */
125 #define SUPER_MUX_STATE_BIT_STDBY 0x00
126 #define SUPER_MUX_STATE_BIT_IDLE 0x01
127 #define SUPER_MUX_STATE_BIT_RUN 0x02
128 #define SUPER_MUX_STATE_BIT_IRQ 0x04
129 #define SUPER_MUX_STATE_BIT_FIQ 0x08
130
131 #define SUPER_MUX_MUX_WIDTH 4
132 #define SUPER_MUX_LP_DIV2_BYPASS (1 << 16)
133
134 static uint32_t
super_mux_get_state(uint32_t reg)135 super_mux_get_state(uint32_t reg)
136 {
137 reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK;
138 if (reg & SUPER_MUX_STATE_BIT_FIQ)
139 return (SUPER_MUX_STATE_FIQ);
140 if (reg & SUPER_MUX_STATE_BIT_IRQ)
141 return (SUPER_MUX_STATE_IRQ);
142 if (reg & SUPER_MUX_STATE_BIT_RUN)
143 return (SUPER_MUX_STATE_RUN);
144 if (reg & SUPER_MUX_STATE_BIT_IDLE)
145 return (SUPER_MUX_STATE_IDLE);
146 return (SUPER_MUX_STATE_STDBY);
147 }
148
149 static int
super_mux_init(struct clknode * clk,device_t dev)150 super_mux_init(struct clknode *clk, device_t dev)
151 {
152 struct super_mux_sc *sc;
153 uint32_t reg;
154 int shift, state;
155
156 sc = clknode_get_softc(clk);
157
158 DEVICE_LOCK(sc);
159 RD4(sc, sc->base_reg, ®);
160 DEVICE_UNLOCK(sc);
161 state = super_mux_get_state(reg);
162
163 if ((state != SUPER_MUX_STATE_RUN) &&
164 (state != SUPER_MUX_STATE_IDLE)) {
165 panic("Unexpected super mux state: %u", state);
166 }
167
168 shift = state * SUPER_MUX_MUX_WIDTH;
169
170 sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1);
171
172 /*
173 * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set
174 * and source mux is set to PLLX.
175 */
176 if (sc->flags & SMF_HAVE_DIVIDER_2) {
177 if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) &&
178 (sc->mux == sc->src_pllx))
179 sc->mux = sc->src_div2;
180 }
181 clknode_init_parent_idx(clk, sc->mux);
182
183 return(0);
184 }
185
186 static int
super_mux_set_mux(struct clknode * clk,int idx)187 super_mux_set_mux(struct clknode *clk, int idx)
188 {
189
190 struct super_mux_sc *sc;
191 int shift, state;
192 uint32_t reg, dummy;
193
194 sc = clknode_get_softc(clk);
195
196 DEVICE_LOCK(sc);
197 RD4(sc, sc->base_reg, ®);
198 state = super_mux_get_state(reg);
199
200 if ((state != SUPER_MUX_STATE_RUN) &&
201 (state != SUPER_MUX_STATE_IDLE)) {
202 panic("Unexpected super mux state: %u", state);
203 }
204 shift = (state - 1) * SUPER_MUX_MUX_WIDTH;
205 sc->mux = idx;
206 if (sc->flags & SMF_HAVE_DIVIDER_2) {
207 if (idx == sc->src_div2) {
208 idx = sc->src_pllx;
209 reg &= ~SUPER_MUX_LP_DIV2_BYPASS;
210 WR4(sc, sc->base_reg, reg);
211 RD4(sc, sc->base_reg, &dummy);
212 } else if (idx == sc->src_pllx) {
213 reg = SUPER_MUX_LP_DIV2_BYPASS;
214 WR4(sc, sc->base_reg, reg);
215 RD4(sc, sc->base_reg, &dummy);
216 }
217 }
218 reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift);
219 reg |= idx << shift;
220
221 WR4(sc, sc->base_reg, reg);
222 RD4(sc, sc->base_reg, &dummy);
223 DEVICE_UNLOCK(sc);
224
225 return(0);
226 }
227
228 static int
super_mux_register(struct clkdom * clkdom,struct super_mux_def * clkdef)229 super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef)
230 {
231 struct clknode *clk;
232 struct super_mux_sc *sc;
233
234 clk = clknode_create(clkdom, &tegra124_super_mux_class,
235 &clkdef->clkdef);
236 if (clk == NULL)
237 return (1);
238
239 sc = clknode_get_softc(clk);
240 sc->clkdev = clknode_get_device(clk);
241 sc->base_reg = clkdef->base_reg;
242 sc->src_pllx = clkdef->src_pllx;
243 sc->src_div2 = clkdef->src_div2;
244 sc->flags = clkdef->flags;
245
246 clknode_register(clkdom, clk);
247 return (0);
248 }
249
250 void
tegra124_super_mux_clock(struct tegra124_car_softc * sc)251 tegra124_super_mux_clock(struct tegra124_car_softc *sc)
252 {
253 int i, rv;
254
255 for (i = 0; i < nitems(super_mux_def); i++) {
256 rv = super_mux_register(sc->clkdom, &super_mux_def[i]);
257 if (rv != 0)
258 panic("super_mux_register failed");
259 }
260
261 }
262