Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/nvidia/tegra124/tegra124_clk_super.c
39507 views
1
/*-
2
* Copyright (c) 2016 Michal Meloun <[email protected]>
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
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
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, &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
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, &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
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
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
263