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