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