1296936Smmel/*- 2296936Smmel * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3296936Smmel * All rights reserved. 4296936Smmel * 5296936Smmel * Redistribution and use in source and binary forms, with or without 6296936Smmel * modification, are permitted provided that the following conditions 7296936Smmel * are met: 8296936Smmel * 1. Redistributions of source code must retain the above copyright 9296936Smmel * notice, this list of conditions and the following disclaimer. 10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright 11296936Smmel * notice, this list of conditions and the following disclaimer in the 12296936Smmel * documentation and/or other materials provided with the distribution. 13296936Smmel * 14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17296936Smmel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24296936Smmel * SUCH DAMAGE. 25296936Smmel */ 26296936Smmel 27296936Smmel#include <sys/cdefs.h> 28296936Smmel__FBSDID("$FreeBSD$"); 29296936Smmel 30296936Smmel#include <sys/param.h> 31296936Smmel#include <sys/systm.h> 32296936Smmel#include <sys/bus.h> 33296936Smmel#include <sys/lock.h> 34296936Smmel#include <sys/mutex.h> 35296936Smmel#include <sys/rman.h> 36296936Smmel 37296936Smmel#include <machine/bus.h> 38296936Smmel 39296936Smmel#include <dev/extres/clk/clk.h> 40296936Smmel 41296936Smmel#include <gnu/dts/include/dt-bindings/clock/tegra124-car.h> 42296936Smmel#include "tegra124_car.h" 43296936Smmel 44296936Smmel 45296936Smmel/* Flags */ 46296936Smmel#define SMF_HAVE_DIVIDER_2 1 47296936Smmel 48296936Smmelstruct super_mux_def { 49296936Smmel struct clknode_init_def clkdef; 50296936Smmel uint32_t base_reg; 51296936Smmel uint32_t flags; 52296936Smmel int src_pllx; 53296936Smmel int src_div2; 54296936Smmel}; 55296936Smmel 56296936Smmel#define PLIST(x) static const char *x[] 57296936Smmel#define SM(_id, cn, pl, r, x, d, f) \ 58296936Smmel{ \ 59296936Smmel .clkdef.id = _id, \ 60296936Smmel .clkdef.name = cn, \ 61296936Smmel .clkdef.parent_names = pl, \ 62296936Smmel .clkdef.parent_cnt = nitems(pl), \ 63296936Smmel .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ 64296936Smmel .base_reg = r, \ 65296936Smmel .src_pllx = x, \ 66296936Smmel .src_div2 = d, \ 67296936Smmel .flags = f, \ 68296936Smmel} 69296936Smmel 70296936SmmelPLIST(cclk_g_parents) = { 71296936Smmel "clk_m", "pllC_out0", "clk_s", "pllM_out0", 72296936Smmel "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0", 73296936Smmel "pllX_out", NULL, NULL, NULL, 74296936Smmel NULL, NULL, NULL,NULL, // "dfllCPU_out0" 75296936Smmel}; 76296936Smmel 77296936SmmelPLIST(cclk_lp_parents) = { 78296936Smmel "clk_m", "pllC_out0", "clk_s", "pllM_out0", 79296936Smmel "pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0", 80296936Smmel "pllX_out", NULL, NULL, NULL, 81296936Smmel NULL, NULL, NULL, NULL, 82296936Smmel "pllX_out0" 83296936Smmel}; 84296936Smmel 85296936SmmelPLIST(sclk_parents) = { 86296936Smmel "clk_m", "pllC_out1", "pllP_out4", "pllP_out0", 87296936Smmel "pllP_out2", "pllC_out0", "clk_s", "pllM_out1", 88296936Smmel}; 89296936Smmel 90296936Smmelstatic struct super_mux_def super_mux_def[] = { 91296936Smmel SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0), 92296936Smmel SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2), 93296936Smmel SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0), 94296936Smmel}; 95296936Smmel 96296936Smmelstatic int super_mux_init(struct clknode *clk, device_t dev); 97296936Smmelstatic int super_mux_set_mux(struct clknode *clk, int idx); 98296936Smmel 99296936Smmelstruct super_mux_sc { 100296936Smmel device_t clkdev; 101296936Smmel uint32_t base_reg; 102296936Smmel int src_pllx; 103296936Smmel int src_div2; 104296936Smmel uint32_t flags; 105296936Smmel 106296936Smmel int mux; 107296936Smmel}; 108296936Smmel 109296936Smmelstatic clknode_method_t super_mux_methods[] = { 110296936Smmel /* Device interface */ 111296936Smmel CLKNODEMETHOD(clknode_init, super_mux_init), 112296936Smmel CLKNODEMETHOD(clknode_set_mux, super_mux_set_mux), 113296936Smmel CLKNODEMETHOD_END 114296936Smmel}; 115296936SmmelDEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods, 116296936Smmel sizeof(struct super_mux_sc), clknode_class); 117296936Smmel 118296936Smmel/* Mux status. */ 119296936Smmel#define SUPER_MUX_STATE_STDBY 0 120296936Smmel#define SUPER_MUX_STATE_IDLE 1 121296936Smmel#define SUPER_MUX_STATE_RUN 2 122296936Smmel#define SUPER_MUX_STATE_IRQ 3 123296936Smmel#define SUPER_MUX_STATE_FIQ 4 124296936Smmel 125296936Smmel/* Mux register bits. */ 126296936Smmel#define SUPER_MUX_STATE_BIT_SHIFT 28 127296936Smmel#define SUPER_MUX_STATE_BIT_MASK 0xF 128296936Smmel/* State is Priority encoded */ 129296936Smmel#define SUPER_MUX_STATE_BIT_STDBY 0x00 130296936Smmel#define SUPER_MUX_STATE_BIT_IDLE 0x01 131296936Smmel#define SUPER_MUX_STATE_BIT_RUN 0x02 132296936Smmel#define SUPER_MUX_STATE_BIT_IRQ 0x04 133296936Smmel#define SUPER_MUX_STATE_BIT_FIQ 0x08 134296936Smmel 135296936Smmel#define SUPER_MUX_MUX_WIDTH 4 136296936Smmel#define SUPER_MUX_LP_DIV2_BYPASS (1 << 16) 137296936Smmel 138296936Smmelstatic uint32_t 139296936Smmelsuper_mux_get_state(uint32_t reg) 140296936Smmel{ 141296936Smmel reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK; 142296936Smmel if (reg & SUPER_MUX_STATE_BIT_FIQ) 143296936Smmel return (SUPER_MUX_STATE_FIQ); 144296936Smmel if (reg & SUPER_MUX_STATE_BIT_IRQ) 145296936Smmel return (SUPER_MUX_STATE_IRQ); 146296936Smmel if (reg & SUPER_MUX_STATE_BIT_RUN) 147296936Smmel return (SUPER_MUX_STATE_RUN); 148296936Smmel if (reg & SUPER_MUX_STATE_BIT_IDLE) 149296936Smmel return (SUPER_MUX_STATE_IDLE); 150296936Smmel return (SUPER_MUX_STATE_STDBY); 151296936Smmel} 152296936Smmel 153296936Smmelstatic int 154296936Smmelsuper_mux_init(struct clknode *clk, device_t dev) 155296936Smmel{ 156296936Smmel struct super_mux_sc *sc; 157296936Smmel uint32_t reg; 158296936Smmel int shift, state; 159296936Smmel 160296936Smmel sc = clknode_get_softc(clk); 161296936Smmel 162296936Smmel DEVICE_LOCK(sc); 163296936Smmel RD4(sc, sc->base_reg, ®); 164296936Smmel DEVICE_UNLOCK(sc); 165296936Smmel state = super_mux_get_state(reg); 166296936Smmel 167296936Smmel if ((state != SUPER_MUX_STATE_RUN) && 168296936Smmel (state != SUPER_MUX_STATE_IDLE)) { 169296936Smmel panic("Unexpected super mux state: %u", state); 170296936Smmel } 171296936Smmel 172296936Smmel shift = state * SUPER_MUX_MUX_WIDTH; 173296936Smmel 174296936Smmel sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1); 175296936Smmel 176296936Smmel /* 177296936Smmel * CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set 178296936Smmel * and source mux is set to PLLX. 179296936Smmel */ 180296936Smmel if (sc->flags & SMF_HAVE_DIVIDER_2) { 181296936Smmel if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) && 182296936Smmel (sc->mux == sc->src_pllx)) 183296936Smmel sc->mux = sc->src_div2; 184296936Smmel } 185296936Smmel clknode_init_parent_idx(clk, sc->mux); 186296936Smmel 187296936Smmel return(0); 188296936Smmel} 189296936Smmel 190296936Smmelstatic int 191296936Smmelsuper_mux_set_mux(struct clknode *clk, int idx) 192296936Smmel{ 193296936Smmel 194296936Smmel struct super_mux_sc *sc; 195296936Smmel int shift, state; 196296936Smmel uint32_t reg, dummy; 197296936Smmel 198296936Smmel sc = clknode_get_softc(clk); 199296936Smmel 200296936Smmel DEVICE_LOCK(sc); 201296936Smmel RD4(sc, sc->base_reg, ®); 202296936Smmel state = super_mux_get_state(reg); 203296936Smmel 204296936Smmel if ((state != SUPER_MUX_STATE_RUN) && 205296936Smmel (state != SUPER_MUX_STATE_IDLE)) { 206296936Smmel panic("Unexpected super mux state: %u", state); 207296936Smmel } 208297576Smmel shift = (state - 1) * SUPER_MUX_MUX_WIDTH; 209296936Smmel sc->mux = idx; 210296936Smmel if (sc->flags & SMF_HAVE_DIVIDER_2) { 211296936Smmel if (idx == sc->src_div2) { 212296936Smmel idx = sc->src_pllx; 213296936Smmel reg &= ~SUPER_MUX_LP_DIV2_BYPASS; 214296936Smmel WR4(sc, sc->base_reg, reg); 215296936Smmel RD4(sc, sc->base_reg, &dummy); 216296936Smmel } else if (idx == sc->src_pllx) { 217296936Smmel reg = SUPER_MUX_LP_DIV2_BYPASS; 218296936Smmel WR4(sc, sc->base_reg, reg); 219296936Smmel RD4(sc, sc->base_reg, &dummy); 220296936Smmel } 221296936Smmel } 222296936Smmel reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift); 223296936Smmel reg |= idx << shift; 224297576Smmel 225296936Smmel WR4(sc, sc->base_reg, reg); 226296936Smmel RD4(sc, sc->base_reg, &dummy); 227296936Smmel DEVICE_UNLOCK(sc); 228296936Smmel 229296936Smmel return(0); 230296936Smmel} 231296936Smmel 232296936Smmelstatic int 233296936Smmelsuper_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef) 234296936Smmel{ 235296936Smmel struct clknode *clk; 236296936Smmel struct super_mux_sc *sc; 237296936Smmel 238296936Smmel clk = clknode_create(clkdom, &tegra124_super_mux_class, 239296936Smmel &clkdef->clkdef); 240296936Smmel if (clk == NULL) 241296936Smmel return (1); 242296936Smmel 243296936Smmel sc = clknode_get_softc(clk); 244296936Smmel sc->clkdev = clknode_get_device(clk); 245296936Smmel sc->base_reg = clkdef->base_reg; 246296936Smmel sc->src_pllx = clkdef->src_pllx; 247296936Smmel sc->src_div2 = clkdef->src_div2; 248296936Smmel sc->flags = clkdef->flags; 249296936Smmel 250296936Smmel clknode_register(clkdom, clk); 251296936Smmel return (0); 252296936Smmel} 253296936Smmel 254296936Smmelvoid 255296936Smmeltegra124_super_mux_clock(struct tegra124_car_softc *sc) 256296936Smmel{ 257296936Smmel int i, rv; 258296936Smmel 259296936Smmel for (i = 0; i < nitems(super_mux_def); i++) { 260296936Smmel rv = super_mux_register(sc->clkdom, &super_mux_def[i]); 261296936Smmel if (rv != 0) 262296936Smmel panic("super_mux_register failed"); 263296936Smmel } 264296936Smmel 265296936Smmel} 266