1296936Smmel/*-
2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3296936Smmel * All rights reserved.
4296936Smmel *
5296936Smmel * Redistribution and use in source and binary forms, with or without
6296936Smmel * modification, are permitted provided that the following conditions
7296936Smmel * are met:
8296936Smmel * 1. Redistributions of source code must retain the above copyright
9296936Smmel *    notice, this list of conditions and the following disclaimer.
10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright
11296936Smmel *    notice, this list of conditions and the following disclaimer in the
12296936Smmel *    documentation and/or other materials provided with the distribution.
13296936Smmel *
14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17296936Smmel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24296936Smmel * SUCH DAMAGE.
25296936Smmel */
26296936Smmel
27296936Smmel#include <sys/cdefs.h>
28296936Smmel__FBSDID("$FreeBSD$");
29296936Smmel
30296936Smmel#include <sys/param.h>
31296936Smmel#include <sys/systm.h>
32296936Smmel#include <sys/bus.h>
33296936Smmel#include <sys/lock.h>
34296936Smmel#include <sys/mutex.h>
35296936Smmel#include <sys/rman.h>
36296936Smmel
37296936Smmel#include <machine/bus.h>
38296936Smmel
39296936Smmel#include <dev/extres/clk/clk.h>
40296936Smmel
41296936Smmel#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h>
42296936Smmel#include "tegra124_car.h"
43296936Smmel
44296936Smmel
45296936Smmel/* Flags */
46296936Smmel#define	SMF_HAVE_DIVIDER_2	1
47296936Smmel
48296936Smmelstruct super_mux_def {
49296936Smmel	struct clknode_init_def	clkdef;
50296936Smmel	uint32_t		base_reg;
51296936Smmel	uint32_t		flags;
52296936Smmel	int			src_pllx;
53296936Smmel	int			src_div2;
54296936Smmel};
55296936Smmel
56296936Smmel#define	PLIST(x) static const char *x[]
57296936Smmel#define	SM(_id, cn, pl, r, x, d, f)				\
58296936Smmel{									\
59296936Smmel	.clkdef.id = _id,						\
60296936Smmel	.clkdef.name = cn,						\
61296936Smmel	.clkdef.parent_names = pl,					\
62296936Smmel	.clkdef.parent_cnt = nitems(pl),				\
63296936Smmel	.clkdef.flags = CLK_NODE_STATIC_STRINGS,				\
64296936Smmel	.base_reg = r,							\
65296936Smmel	.src_pllx = x,							\
66296936Smmel	.src_div2 = d,							\
67296936Smmel	.flags = f,							\
68296936Smmel}
69296936Smmel
70296936SmmelPLIST(cclk_g_parents) = {
71296936Smmel	"clk_m", "pllC_out0", "clk_s", "pllM_out0",
72296936Smmel	"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
73296936Smmel	"pllX_out", NULL, NULL, NULL,
74296936Smmel	NULL, NULL, NULL,NULL, // "dfllCPU_out0"
75296936Smmel};
76296936Smmel
77296936SmmelPLIST(cclk_lp_parents) = {
78296936Smmel	"clk_m", "pllC_out0", "clk_s", "pllM_out0",
79296936Smmel	"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",
80296936Smmel	"pllX_out", NULL, NULL, NULL,
81296936Smmel	NULL, NULL, NULL, NULL,
82296936Smmel	"pllX_out0"
83296936Smmel};
84296936Smmel
85296936SmmelPLIST(sclk_parents) = {
86296936Smmel	"clk_m", "pllC_out1", "pllP_out4", "pllP_out0",
87296936Smmel	"pllP_out2", "pllC_out0", "clk_s", "pllM_out1",
88296936Smmel};
89296936Smmel
90296936Smmelstatic struct super_mux_def super_mux_def[] = {
91296936Smmel SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0),
92296936Smmel SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2),
93296936Smmel SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0),
94296936Smmel};
95296936Smmel
96296936Smmelstatic int super_mux_init(struct clknode *clk, device_t dev);
97296936Smmelstatic int super_mux_set_mux(struct clknode *clk, int idx);
98296936Smmel
99296936Smmelstruct super_mux_sc {
100296936Smmel	device_t		clkdev;
101296936Smmel	uint32_t		base_reg;
102296936Smmel	int			src_pllx;
103296936Smmel	int			src_div2;
104296936Smmel	uint32_t		flags;
105296936Smmel
106296936Smmel	int 			mux;
107296936Smmel};
108296936Smmel
109296936Smmelstatic clknode_method_t super_mux_methods[] = {
110296936Smmel	/* Device interface */
111296936Smmel	CLKNODEMETHOD(clknode_init,		super_mux_init),
112296936Smmel	CLKNODEMETHOD(clknode_set_mux, 		super_mux_set_mux),
113296936Smmel	CLKNODEMETHOD_END
114296936Smmel};
115296936SmmelDEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods,
116296936Smmel   sizeof(struct super_mux_sc), clknode_class);
117296936Smmel
118296936Smmel/* Mux status. */
119296936Smmel#define	SUPER_MUX_STATE_STDBY		0
120296936Smmel#define	SUPER_MUX_STATE_IDLE		1
121296936Smmel#define	SUPER_MUX_STATE_RUN		2
122296936Smmel#define	SUPER_MUX_STATE_IRQ		3
123296936Smmel#define	SUPER_MUX_STATE_FIQ		4
124296936Smmel
125296936Smmel/* Mux register bits. */
126296936Smmel#define	SUPER_MUX_STATE_BIT_SHIFT	28
127296936Smmel#define	SUPER_MUX_STATE_BIT_MASK	0xF
128296936Smmel/* State is Priority encoded */
129296936Smmel#define	SUPER_MUX_STATE_BIT_STDBY	0x00
130296936Smmel#define	SUPER_MUX_STATE_BIT_IDLE	0x01
131296936Smmel#define	SUPER_MUX_STATE_BIT_RUN		0x02
132296936Smmel#define	SUPER_MUX_STATE_BIT_IRQ		0x04
133296936Smmel#define	SUPER_MUX_STATE_BIT_FIQ		0x08
134296936Smmel
135296936Smmel#define	SUPER_MUX_MUX_WIDTH		4
136296936Smmel#define	SUPER_MUX_LP_DIV2_BYPASS	(1 << 16)
137296936Smmel
138296936Smmelstatic uint32_t
139296936Smmelsuper_mux_get_state(uint32_t reg)
140296936Smmel{
141296936Smmel	reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK;
142296936Smmel	if (reg & SUPER_MUX_STATE_BIT_FIQ)
143296936Smmel		 return (SUPER_MUX_STATE_FIQ);
144296936Smmel	if (reg & SUPER_MUX_STATE_BIT_IRQ)
145296936Smmel		 return (SUPER_MUX_STATE_IRQ);
146296936Smmel	if (reg & SUPER_MUX_STATE_BIT_RUN)
147296936Smmel		 return (SUPER_MUX_STATE_RUN);
148296936Smmel	if (reg & SUPER_MUX_STATE_BIT_IDLE)
149296936Smmel		 return (SUPER_MUX_STATE_IDLE);
150296936Smmel	return (SUPER_MUX_STATE_STDBY);
151296936Smmel}
152296936Smmel
153296936Smmelstatic int
154296936Smmelsuper_mux_init(struct clknode *clk, device_t dev)
155296936Smmel{
156296936Smmel	struct super_mux_sc *sc;
157296936Smmel	uint32_t reg;
158296936Smmel	int shift, state;
159296936Smmel
160296936Smmel	sc = clknode_get_softc(clk);
161296936Smmel
162296936Smmel	DEVICE_LOCK(sc);
163296936Smmel	RD4(sc, sc->base_reg, &reg);
164296936Smmel	DEVICE_UNLOCK(sc);
165296936Smmel	state = super_mux_get_state(reg);
166296936Smmel
167296936Smmel	if ((state != SUPER_MUX_STATE_RUN) &&
168296936Smmel	    (state != SUPER_MUX_STATE_IDLE)) {
169296936Smmel		panic("Unexpected super mux state: %u", state);
170296936Smmel	}
171296936Smmel
172296936Smmel	shift = state * SUPER_MUX_MUX_WIDTH;
173296936Smmel
174296936Smmel	sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1);
175296936Smmel
176296936Smmel	/*
177296936Smmel	 * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set
178296936Smmel	 * and source mux is set to PLLX.
179296936Smmel	 */
180296936Smmel	if (sc->flags & SMF_HAVE_DIVIDER_2) {
181296936Smmel		if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) &&
182296936Smmel		    (sc->mux == sc->src_pllx))
183296936Smmel		sc->mux = sc->src_div2;
184296936Smmel	}
185296936Smmel	clknode_init_parent_idx(clk, sc->mux);
186296936Smmel
187296936Smmel	return(0);
188296936Smmel}
189296936Smmel
190296936Smmelstatic int
191296936Smmelsuper_mux_set_mux(struct clknode *clk, int idx)
192296936Smmel{
193296936Smmel
194296936Smmel	struct super_mux_sc *sc;
195296936Smmel	int shift, state;
196296936Smmel	uint32_t reg, dummy;
197296936Smmel
198296936Smmel	sc = clknode_get_softc(clk);
199296936Smmel
200296936Smmel	DEVICE_LOCK(sc);
201296936Smmel	RD4(sc, sc->base_reg, &reg);
202296936Smmel	state = super_mux_get_state(reg);
203296936Smmel
204296936Smmel	if ((state != SUPER_MUX_STATE_RUN) &&
205296936Smmel	    (state != SUPER_MUX_STATE_IDLE)) {
206296936Smmel		panic("Unexpected super mux state: %u", state);
207296936Smmel	}
208297576Smmel	shift = (state - 1) * SUPER_MUX_MUX_WIDTH;
209296936Smmel	sc->mux = idx;
210296936Smmel	if (sc->flags & SMF_HAVE_DIVIDER_2) {
211296936Smmel		if (idx == sc->src_div2) {
212296936Smmel			idx = sc->src_pllx;
213296936Smmel			reg &= ~SUPER_MUX_LP_DIV2_BYPASS;
214296936Smmel			WR4(sc, sc->base_reg, reg);
215296936Smmel			RD4(sc, sc->base_reg, &dummy);
216296936Smmel		} else if (idx == sc->src_pllx) {
217296936Smmel			reg = SUPER_MUX_LP_DIV2_BYPASS;
218296936Smmel			WR4(sc, sc->base_reg, reg);
219296936Smmel			RD4(sc, sc->base_reg, &dummy);
220296936Smmel		}
221296936Smmel	}
222296936Smmel	reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift);
223296936Smmel	reg |= idx << shift;
224297576Smmel
225296936Smmel	WR4(sc, sc->base_reg, reg);
226296936Smmel	RD4(sc, sc->base_reg, &dummy);
227296936Smmel	DEVICE_UNLOCK(sc);
228296936Smmel
229296936Smmel	return(0);
230296936Smmel}
231296936Smmel
232296936Smmelstatic int
233296936Smmelsuper_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef)
234296936Smmel{
235296936Smmel	struct clknode *clk;
236296936Smmel	struct super_mux_sc *sc;
237296936Smmel
238296936Smmel	clk = clknode_create(clkdom, &tegra124_super_mux_class,
239296936Smmel	    &clkdef->clkdef);
240296936Smmel	if (clk == NULL)
241296936Smmel		return (1);
242296936Smmel
243296936Smmel	sc = clknode_get_softc(clk);
244296936Smmel	sc->clkdev = clknode_get_device(clk);
245296936Smmel	sc->base_reg = clkdef->base_reg;
246296936Smmel	sc->src_pllx = clkdef->src_pllx;
247296936Smmel	sc->src_div2 = clkdef->src_div2;
248296936Smmel	sc->flags = clkdef->flags;
249296936Smmel
250296936Smmel	clknode_register(clkdom, clk);
251296936Smmel	return (0);
252296936Smmel}
253296936Smmel
254296936Smmelvoid
255296936Smmeltegra124_super_mux_clock(struct tegra124_car_softc *sc)
256296936Smmel{
257296936Smmel	int i, rv;
258296936Smmel
259296936Smmel	for (i = 0; i <  nitems(super_mux_def); i++) {
260296936Smmel		rv = super_mux_register(sc->clkdom, &super_mux_def[i]);
261296936Smmel		if (rv != 0)
262296936Smmel			panic("super_mux_register failed");
263296936Smmel	}
264296936Smmel
265296936Smmel}
266