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