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