Path: blob/main/sys/arm/nvidia/tegra124/tegra124_clk_super.c
39507 views
/*-1* Copyright (c) 2016 Michal Meloun <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/systm.h>28#include <sys/bus.h>29#include <sys/lock.h>30#include <sys/mutex.h>31#include <sys/rman.h>3233#include <machine/bus.h>3435#include <dev/clk/clk.h>3637#include <dt-bindings/clock/tegra124-car.h>38#include "tegra124_car.h"3940/* Flags */41#define SMF_HAVE_DIVIDER_2 14243struct super_mux_def {44struct clknode_init_def clkdef;45uint32_t base_reg;46uint32_t flags;47int src_pllx;48int src_div2;49};5051#define PLIST(x) static const char *x[]52#define SM(_id, cn, pl, r, x, d, f) \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.src_pllx = x, \61.src_div2 = d, \62.flags = f, \63}6465PLIST(cclk_g_parents) = {66"clk_m", "pllC_out0", "clk_s", "pllM_out0",67"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",68"pllX_out", NULL, NULL, NULL,69NULL, NULL, NULL,NULL, // "dfllCPU_out0"70};7172PLIST(cclk_lp_parents) = {73"clk_m", "pllC_out0", "clk_s", "pllM_out0",74"pllP_out0", "pllP_out4", "pllC2_out0", "pllC3_out0",75"pllX_out", NULL, NULL, NULL,76NULL, NULL, NULL, NULL,77"pllX_out0"78};7980PLIST(sclk_parents) = {81"clk_m", "pllC_out1", "pllP_out4", "pllP_out0",82"pllP_out2", "pllC_out0", "clk_s", "pllM_out1",83};8485static struct super_mux_def super_mux_def[] = {86SM(TEGRA124_CLK_CCLK_G, "cclk_g", cclk_g_parents, CCLKG_BURST_POLICY, 0, 0, 0),87SM(TEGRA124_CLK_CCLK_LP, "cclk_lp", cclk_lp_parents, CCLKLP_BURST_POLICY, 8, 16, SMF_HAVE_DIVIDER_2),88SM(TEGRA124_CLK_SCLK, "sclk", sclk_parents, SCLK_BURST_POLICY, 0, 0, 0),89};9091static int super_mux_init(struct clknode *clk, device_t dev);92static int super_mux_set_mux(struct clknode *clk, int idx);9394struct super_mux_sc {95device_t clkdev;96uint32_t base_reg;97int src_pllx;98int src_div2;99uint32_t flags;100101int mux;102};103104static clknode_method_t super_mux_methods[] = {105/* Device interface */106CLKNODEMETHOD(clknode_init, super_mux_init),107CLKNODEMETHOD(clknode_set_mux, super_mux_set_mux),108CLKNODEMETHOD_END109};110DEFINE_CLASS_1(tegra124_super_mux, tegra124_super_mux_class, super_mux_methods,111sizeof(struct super_mux_sc), clknode_class);112113/* Mux status. */114#define SUPER_MUX_STATE_STDBY 0115#define SUPER_MUX_STATE_IDLE 1116#define SUPER_MUX_STATE_RUN 2117#define SUPER_MUX_STATE_IRQ 3118#define SUPER_MUX_STATE_FIQ 4119120/* Mux register bits. */121#define SUPER_MUX_STATE_BIT_SHIFT 28122#define SUPER_MUX_STATE_BIT_MASK 0xF123/* State is Priority encoded */124#define SUPER_MUX_STATE_BIT_STDBY 0x00125#define SUPER_MUX_STATE_BIT_IDLE 0x01126#define SUPER_MUX_STATE_BIT_RUN 0x02127#define SUPER_MUX_STATE_BIT_IRQ 0x04128#define SUPER_MUX_STATE_BIT_FIQ 0x08129130#define SUPER_MUX_MUX_WIDTH 4131#define SUPER_MUX_LP_DIV2_BYPASS (1 << 16)132133static uint32_t134super_mux_get_state(uint32_t reg)135{136reg = (reg >> SUPER_MUX_STATE_BIT_SHIFT) & SUPER_MUX_STATE_BIT_MASK;137if (reg & SUPER_MUX_STATE_BIT_FIQ)138return (SUPER_MUX_STATE_FIQ);139if (reg & SUPER_MUX_STATE_BIT_IRQ)140return (SUPER_MUX_STATE_IRQ);141if (reg & SUPER_MUX_STATE_BIT_RUN)142return (SUPER_MUX_STATE_RUN);143if (reg & SUPER_MUX_STATE_BIT_IDLE)144return (SUPER_MUX_STATE_IDLE);145return (SUPER_MUX_STATE_STDBY);146}147148static int149super_mux_init(struct clknode *clk, device_t dev)150{151struct super_mux_sc *sc;152uint32_t reg;153int shift, state;154155sc = clknode_get_softc(clk);156157DEVICE_LOCK(sc);158RD4(sc, sc->base_reg, ®);159DEVICE_UNLOCK(sc);160state = super_mux_get_state(reg);161162if ((state != SUPER_MUX_STATE_RUN) &&163(state != SUPER_MUX_STATE_IDLE)) {164panic("Unexpected super mux state: %u", state);165}166167shift = state * SUPER_MUX_MUX_WIDTH;168169sc->mux = (reg >> shift) & ((1 << SUPER_MUX_MUX_WIDTH) - 1);170171/*172* CCLKLP uses PLLX/2 as source if LP_DIV2_BYPASS isn't set173* and source mux is set to PLLX.174*/175if (sc->flags & SMF_HAVE_DIVIDER_2) {176if (((reg & SUPER_MUX_LP_DIV2_BYPASS) == 0) &&177(sc->mux == sc->src_pllx))178sc->mux = sc->src_div2;179}180clknode_init_parent_idx(clk, sc->mux);181182return(0);183}184185static int186super_mux_set_mux(struct clknode *clk, int idx)187{188189struct super_mux_sc *sc;190int shift, state;191uint32_t reg, dummy;192193sc = clknode_get_softc(clk);194195DEVICE_LOCK(sc);196RD4(sc, sc->base_reg, ®);197state = super_mux_get_state(reg);198199if ((state != SUPER_MUX_STATE_RUN) &&200(state != SUPER_MUX_STATE_IDLE)) {201panic("Unexpected super mux state: %u", state);202}203shift = (state - 1) * SUPER_MUX_MUX_WIDTH;204sc->mux = idx;205if (sc->flags & SMF_HAVE_DIVIDER_2) {206if (idx == sc->src_div2) {207idx = sc->src_pllx;208reg &= ~SUPER_MUX_LP_DIV2_BYPASS;209WR4(sc, sc->base_reg, reg);210RD4(sc, sc->base_reg, &dummy);211} else if (idx == sc->src_pllx) {212reg = SUPER_MUX_LP_DIV2_BYPASS;213WR4(sc, sc->base_reg, reg);214RD4(sc, sc->base_reg, &dummy);215}216}217reg &= ~(((1 << SUPER_MUX_MUX_WIDTH) - 1) << shift);218reg |= idx << shift;219220WR4(sc, sc->base_reg, reg);221RD4(sc, sc->base_reg, &dummy);222DEVICE_UNLOCK(sc);223224return(0);225}226227static int228super_mux_register(struct clkdom *clkdom, struct super_mux_def *clkdef)229{230struct clknode *clk;231struct super_mux_sc *sc;232233clk = clknode_create(clkdom, &tegra124_super_mux_class,234&clkdef->clkdef);235if (clk == NULL)236return (1);237238sc = clknode_get_softc(clk);239sc->clkdev = clknode_get_device(clk);240sc->base_reg = clkdef->base_reg;241sc->src_pllx = clkdef->src_pllx;242sc->src_div2 = clkdef->src_div2;243sc->flags = clkdef->flags;244245clknode_register(clkdom, clk);246return (0);247}248249void250tegra124_super_mux_clock(struct tegra124_car_softc *sc)251{252int i, rv;253254for (i = 0; i < nitems(super_mux_def); i++) {255rv = super_mux_register(sc->clkdom, &super_mux_def[i]);256if (rv != 0)257panic("super_mux_register failed");258}259260}261262263