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, ®); 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, ®); 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 } 266