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