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/cdefs.h>
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/lock.h>
32 #include <sys/mutex.h>
33 #include <sys/rman.h>
34 
35 #include <machine/bus.h>
36 
37 #include <dev/extres/clk/clk.h>
38 
39 #include <dt-bindings/clock/tegra124-car.h>
40 #include "tegra124_car.h"
41 
42 /* Flags */
43 #define	SMF_HAVE_DIVIDER_2	1
44 
45 struct super_mux_def {
46 	struct clknode_init_def	clkdef;
47 	uint32_t		base_reg;
48 	uint32_t		flags;
49 	int			src_pllx;
50 	int			src_div2;
51 };
52 
53 #define	PLIST(x) static const char *x[]
54 #define	SM(_id, cn, pl, r, x, d, f)				\
55 {									\
56 	.clkdef.id = _id,						\
57 	.clkdef.name = cn,						\
58 	.clkdef.parent_names = pl,					\
59 	.clkdef.parent_cnt = nitems(pl),				\
60 	.clkdef.flags = CLK_NODE_STATIC_STRINGS,				\
61 	.base_reg = r,							\
62 	.src_pllx = x,							\
63 	.src_div2 = d,							\
64 	.flags = f,							\
65 }
66 
67 PLIST(cclk_g_parents) = {
68 	"clk_m", "pllC_out0", "clk_s", "pllM_out0",
69 	"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
70 	"pllX_out", NULL, NULL, NULL,
71 	NULL, NULL, NULL,NULL, // "dfllCPU_out0"
72 };
73 
74 PLIST(cclk_lp_parents) = {
75 	"clk_m", "pllC_out0", "clk_s", "pllM_out0",
76 	"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
77 	"pllX_out", NULL, NULL, NULL,
78 	NULL, NULL, NULL, NULL,
79 	"pllX_out0"
80 };
81 
82 PLIST(sclk_parents) = {
83 	"clk_m", "pllC_out1", "pllP_out4", "pllP_out0",
84 	"pllP_out2", "pllC_out0", "clk_s", "pllM_out1",
85 };
86 
87 static struct super_mux_def super_mux_def[] = {
88  SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0),
89  SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2),
90  SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0),
91 };
92 
93 static int super_mux_init(struct clknode *clk, device_t dev);
94 static int super_mux_set_mux(struct clknode *clk, int idx);
95 
96 struct super_mux_sc {
97 	device_t		clkdev;
98 	uint32_t		base_reg;
99 	int			src_pllx;
100 	int			src_div2;
101 	uint32_t		flags;
102 
103 	int 			mux;
104 };
105 
106 static clknode_method_t super_mux_methods[] = {
107 	/* Device interface */
108 	CLKNODEMETHOD(clknode_init,		super_mux_init),
109 	CLKNODEMETHOD(clknode_set_mux, 		super_mux_set_mux),
110 	CLKNODEMETHOD_END
111 };
112 DEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods,
113    sizeof(struct super_mux_sc), clknode_class);
114 
115 /* Mux status. */
116 #define	SUPER_MUX_STATE_STDBY		0
117 #define	SUPER_MUX_STATE_IDLE		1
118 #define	SUPER_MUX_STATE_RUN		2
119 #define	SUPER_MUX_STATE_IRQ		3
120 #define	SUPER_MUX_STATE_FIQ		4
121 
122 /* Mux register bits. */
123 #define	SUPER_MUX_STATE_BIT_SHIFT	28
124 #define	SUPER_MUX_STATE_BIT_MASK	0xF
125 /* State is Priority encoded */
126 #define	SUPER_MUX_STATE_BIT_STDBY	0x00
127 #define	SUPER_MUX_STATE_BIT_IDLE	0x01
128 #define	SUPER_MUX_STATE_BIT_RUN		0x02
129 #define	SUPER_MUX_STATE_BIT_IRQ		0x04
130 #define	SUPER_MUX_STATE_BIT_FIQ		0x08
131 
132 #define	SUPER_MUX_MUX_WIDTH		4
133 #define	SUPER_MUX_LP_DIV2_BYPASS	(1 << 16)
134 
135 static uint32_t
136 super_mux_get_state(uint32_t reg)
137 {
138 	reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK;
139 	if (reg & SUPER_MUX_STATE_BIT_FIQ)
140 		 return (SUPER_MUX_STATE_FIQ);
141 	if (reg & SUPER_MUX_STATE_BIT_IRQ)
142 		 return (SUPER_MUX_STATE_IRQ);
143 	if (reg & SUPER_MUX_STATE_BIT_RUN)
144 		 return (SUPER_MUX_STATE_RUN);
145 	if (reg & SUPER_MUX_STATE_BIT_IDLE)
146 		 return (SUPER_MUX_STATE_IDLE);
147 	return (SUPER_MUX_STATE_STDBY);
148 }
149 
150 static int
151 super_mux_init(struct clknode *clk, device_t dev)
152 {
153 	struct super_mux_sc *sc;
154 	uint32_t reg;
155 	int shift, state;
156 
157 	sc = clknode_get_softc(clk);
158 
159 	DEVICE_LOCK(sc);
160 	RD4(sc, sc->base_reg, &reg);
161 	DEVICE_UNLOCK(sc);
162 	state = super_mux_get_state(reg);
163 
164 	if ((state != SUPER_MUX_STATE_RUN) &&
165 	    (state != SUPER_MUX_STATE_IDLE)) {
166 		panic("Unexpected super mux state: %u", state);
167 	}
168 
169 	shift = state * SUPER_MUX_MUX_WIDTH;
170 
171 	sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1);
172 
173 	/*
174 	 * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set
175 	 * and source mux is set to PLLX.
176 	 */
177 	if (sc->flags & SMF_HAVE_DIVIDER_2) {
178 		if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) &&
179 		    (sc->mux == sc->src_pllx))
180 		sc->mux = sc->src_div2;
181 	}
182 	clknode_init_parent_idx(clk, sc->mux);
183 
184 	return(0);
185 }
186 
187 static int
188 super_mux_set_mux(struct clknode *clk, int idx)
189 {
190 
191 	struct super_mux_sc *sc;
192 	int shift, state;
193 	uint32_t reg, dummy;
194 
195 	sc = clknode_get_softc(clk);
196 
197 	DEVICE_LOCK(sc);
198 	RD4(sc, sc->base_reg, &reg);
199 	state = super_mux_get_state(reg);
200 
201 	if ((state != SUPER_MUX_STATE_RUN) &&
202 	    (state != SUPER_MUX_STATE_IDLE)) {
203 		panic("Unexpected super mux state: %u", state);
204 	}
205 	shift = (state - 1) * SUPER_MUX_MUX_WIDTH;
206 	sc->mux = idx;
207 	if (sc->flags & SMF_HAVE_DIVIDER_2) {
208 		if (idx == sc->src_div2) {
209 			idx = sc->src_pllx;
210 			reg &= ~SUPER_MUX_LP_DIV2_BYPASS;
211 			WR4(sc, sc->base_reg, reg);
212 			RD4(sc, sc->base_reg, &dummy);
213 		} else if (idx == sc->src_pllx) {
214 			reg = SUPER_MUX_LP_DIV2_BYPASS;
215 			WR4(sc, sc->base_reg, reg);
216 			RD4(sc, sc->base_reg, &dummy);
217 		}
218 	}
219 	reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift);
220 	reg |= idx << shift;
221 
222 	WR4(sc, sc->base_reg, reg);
223 	RD4(sc, sc->base_reg, &dummy);
224 	DEVICE_UNLOCK(sc);
225 
226 	return(0);
227 }
228 
229 static int
230 super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef)
231 {
232 	struct clknode *clk;
233 	struct super_mux_sc *sc;
234 
235 	clk = clknode_create(clkdom, &tegra124_super_mux_class,
236 	    &clkdef->clkdef);
237 	if (clk == NULL)
238 		return (1);
239 
240 	sc = clknode_get_softc(clk);
241 	sc->clkdev = clknode_get_device(clk);
242 	sc->base_reg = clkdef->base_reg;
243 	sc->src_pllx = clkdef->src_pllx;
244 	sc->src_div2 = clkdef->src_div2;
245 	sc->flags = clkdef->flags;
246 
247 	clknode_register(clkdom, clk);
248 	return (0);
249 }
250 
251 void
252 tegra124_super_mux_clock(struct tegra124_car_softc *sc)
253 {
254 	int i, rv;
255 
256 	for (i = 0; i <  nitems(super_mux_def); i++) {
257 		rv = super_mux_register(sc->clkdom, &super_mux_def[i]);
258 		if (rv != 0)
259 			panic("super_mux_register failed");
260 	}
261 
262 }
263