xref: /freebsd/sys/arm64/nvidia/tegra210/tegra210_clk_super.c (revision 5e3190f700637fcfc1a52daeaa4a031fdd2557c7)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright 2020 Michal Meloun <mmel@FreeBSD.org>
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/cdefs.h>
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/bus.h>
32 #include <sys/lock.h>
33 #include <sys/mutex.h>
34 #include <sys/rman.h>
35 
36 #include <machine/bus.h>
37 
38 #include <dev/extres/clk/clk.h>
39 
40 #include <dt-bindings/clock/tegra210-car.h>
41 #include "tegra210_car.h"
42 
43 struct super_mux_def {
44 	struct clknode_init_def	clkdef;
45 	uint32_t		base_reg;
46 	uint32_t		flags;
47 };
48 
49 #define	PLIST(x) static const char *x[]
50 #define	SM(_id, cn, pl, r)						\
51 {									\
52 	.clkdef.id = _id,						\
53 	.clkdef.name = cn,						\
54 	.clkdef.parent_names = pl,					\
55 	.clkdef.parent_cnt = nitems(pl),				\
56 	.clkdef.flags = CLK_NODE_STATIC_STRINGS,			\
57 	.base_reg = r,							\
58 }
59 
60 
61 PLIST(cclk_g_parents) = {
62 	"clk_m", NULL, "clk_s", NULL,
63 	"pllP_out0", "pllP_out4", NULL, NULL,
64 	"pllX_out0", "dfllCPU_out_alias", NULL, NULL,
65 	NULL, NULL, "pllX_out0_alias", "dfllCPU_out",
66 };
67 
68 PLIST(cclk_lp_parents) = {
69 	"clk_m", NULL, "clk_s", NULL,
70 	"pllP_out0", "pllP_out4", NULL, NULL,
71 	"pllX_out0", "dfllCPU_out_alias", NULL, NULL,
72 	NULL, NULL, "pllX_out0_alias", "dfllCPU_out",
73 };
74 
75 PLIST(sclk_parents) = {
76 	"clk_m", "pllC_out1", "pllC4_out3", "pllP_out0",
77 	"pllP_out2", "pllC4_out1", "clk_s", "pllC4_out1",
78 };
79 
80 static struct super_mux_def super_mux_def[] = {
81  SM(TEGRA210_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY),
82  SM(TEGRA210_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY),
83  SM(TEGRA210_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY),
84 };
85 
86 static int super_mux_init(struct clknode *clk, device_t dev);
87 static int super_mux_set_mux(struct clknode *clk, int idx);
88 
89 struct super_mux_sc {
90 	device_t		clkdev;
91 	uint32_t		base_reg;
92 	uint32_t		flags;
93 
94 	int 			mux;
95 };
96 
97 static clknode_method_t super_mux_methods[] = {
98 	/* Device interface */
99 	CLKNODEMETHOD(clknode_init,		super_mux_init),
100 	CLKNODEMETHOD(clknode_set_mux, 		super_mux_set_mux),
101 	CLKNODEMETHOD_END
102 };
103 DEFINE_CLASS_1(tegra210_super_mux, tegra210_super_mux_class, super_mux_methods,
104    sizeof(struct super_mux_sc), clknode_class);
105 
106 /* Mux status. */
107 #define	SUPER_MUX_STATE_STDBY		0
108 #define	SUPER_MUX_STATE_IDLE		1
109 #define	SUPER_MUX_STATE_RUN		2
110 #define	SUPER_MUX_STATE_IRQ		3
111 #define	SUPER_MUX_STATE_FIQ		4
112 
113 /* Mux register bits. */
114 #define	SUPER_MUX_STATE_BIT_SHIFT	28
115 #define	SUPER_MUX_STATE_BIT_MASK	0xF
116 /* State is Priority encoded */
117 #define	SUPER_MUX_STATE_BIT_STDBY	0x00
118 #define	SUPER_MUX_STATE_BIT_IDLE	0x01
119 #define	SUPER_MUX_STATE_BIT_RUN		0x02
120 #define	SUPER_MUX_STATE_BIT_IRQ		0x04
121 #define	SUPER_MUX_STATE_BIT_FIQ		0x08
122 
123 #define	SUPER_MUX_MUX_WIDTH		4
124 
125 static uint32_t
126 super_mux_get_state(uint32_t reg)
127 {
128 	reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK;
129 	if (reg & SUPER_MUX_STATE_BIT_FIQ)
130 		 return (SUPER_MUX_STATE_FIQ);
131 	if (reg & SUPER_MUX_STATE_BIT_IRQ)
132 		 return (SUPER_MUX_STATE_IRQ);
133 	if (reg & SUPER_MUX_STATE_BIT_RUN)
134 		 return (SUPER_MUX_STATE_RUN);
135 	if (reg & SUPER_MUX_STATE_BIT_IDLE)
136 		 return (SUPER_MUX_STATE_IDLE);
137 	return (SUPER_MUX_STATE_STDBY);
138 }
139 
140 static int
141 super_mux_init(struct clknode *clk, device_t dev)
142 {
143 	struct super_mux_sc *sc;
144 	uint32_t reg;
145 	int shift, state;
146 
147 	sc = clknode_get_softc(clk);
148 
149 	DEVICE_LOCK(sc);
150 	RD4(sc, sc->base_reg, &reg);
151 	DEVICE_UNLOCK(sc);
152 	state = super_mux_get_state(reg);
153 
154 	if ((state != SUPER_MUX_STATE_RUN) &&
155 	    (state != SUPER_MUX_STATE_IDLE)) {
156 		panic("Unexpected super mux state: %u", state);
157 	}
158 
159 	shift = state * SUPER_MUX_MUX_WIDTH;
160 	sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1);
161 
162 	clknode_init_parent_idx(clk, sc->mux);
163 
164 	return(0);
165 }
166 
167 static int
168 super_mux_set_mux(struct clknode *clk, int idx)
169 {
170 
171 	struct super_mux_sc *sc;
172 	int shift, state;
173 	uint32_t reg, dummy;
174 
175 	sc = clknode_get_softc(clk);
176 
177 	DEVICE_LOCK(sc);
178 	RD4(sc, sc->base_reg, &reg);
179 	state = super_mux_get_state(reg);
180 
181 	if ((state != SUPER_MUX_STATE_RUN) &&
182 	    (state != SUPER_MUX_STATE_IDLE)) {
183 		panic("Unexpected super mux state: %u", state);
184 	}
185 
186 	shift = (state - 1) * SUPER_MUX_MUX_WIDTH;
187 	sc->mux = idx;
188 	reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift);
189 	reg |= idx << shift;
190 
191 	WR4(sc, sc->base_reg, reg);
192 	RD4(sc, sc->base_reg, &dummy);
193 	DEVICE_UNLOCK(sc);
194 
195 	return(0);
196 }
197 
198 static int
199 super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef)
200 {
201 	struct clknode *clk;
202 	struct super_mux_sc *sc;
203 
204 	clk = clknode_create(clkdom, &tegra210_super_mux_class,
205 	    &clkdef->clkdef);
206 	if (clk == NULL)
207 		return (1);
208 
209 	sc = clknode_get_softc(clk);
210 	sc->clkdev = clknode_get_device(clk);
211 	sc->base_reg = clkdef->base_reg;
212 	sc->flags = clkdef->flags;
213 
214 	clknode_register(clkdom, clk);
215 	return (0);
216 }
217 
218 void
219 tegra210_super_mux_clock(struct tegra210_car_softc *sc)
220 {
221 	int i, rv;
222 
223 	for (i = 0; i <  nitems(super_mux_def); i++) {
224 		rv = super_mux_register(sc->clkdom, &super_mux_def[i]);
225 		if (rv != 0)
226 			panic("super_mux_register failed");
227 	}
228 
229 }
230