Path: blob/main/sys/dev/clk/rockchip/rk_clk_armclk.c
39537 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2018 Emmanuel Vadot <[email protected]>4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,19* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;20* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED21* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,22* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/systm.h>29#include <sys/bus.h>3031#include <dev/clk/clk.h>3233#include <dev/clk/rockchip/rk_clk_armclk.h>3435#include "clkdev_if.h"3637struct rk_clk_armclk_sc {38uint32_t muxdiv_offset;39uint32_t mux_shift;40uint32_t mux_width;41uint32_t mux_mask;4243uint32_t div_shift;44uint32_t div_width;45uint32_t div_mask;4647uint32_t gate_offset;48uint32_t gate_shift;4950uint32_t flags;5152uint32_t main_parent;53uint32_t alt_parent;5455struct rk_clk_armclk_rates *rates;56int nrates;57};5859#define WRITE4(_clk, off, val) \60CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)61#define READ4(_clk, off, val) \62CLKDEV_READ_4(clknode_get_device(_clk), off, val)63#define DEVICE_LOCK(_clk) \64CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))65#define DEVICE_UNLOCK(_clk) \66CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))6768#define RK_ARMCLK_WRITE_MASK_SHIFT 166970#if 071#define dprintf(format, arg...) \72printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)73#else74#define dprintf(format, arg...)75#endif7677static int78rk_clk_armclk_init(struct clknode *clk, device_t dev)79{80struct rk_clk_armclk_sc *sc;81uint32_t val, idx;8283sc = clknode_get_softc(clk);8485idx = 0;86DEVICE_LOCK(clk);87READ4(clk, sc->muxdiv_offset, &val);88DEVICE_UNLOCK(clk);8990idx = (val & sc->mux_mask) >> sc->mux_shift;9192clknode_init_parent_idx(clk, idx);9394return (0);95}9697static int98rk_clk_armclk_set_mux(struct clknode *clk, int index)99{100struct rk_clk_armclk_sc *sc;101uint32_t val = 0;102103sc = clknode_get_softc(clk);104105dprintf("Set mux to %d\n", index);106DEVICE_LOCK(clk);107val |= index << sc->mux_shift;108val |= sc->mux_mask << RK_ARMCLK_WRITE_MASK_SHIFT;109dprintf("Write: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, val);110WRITE4(clk, sc->muxdiv_offset, val);111DEVICE_UNLOCK(clk);112113return (0);114}115116static int117rk_clk_armclk_recalc(struct clknode *clk, uint64_t *freq)118{119struct rk_clk_armclk_sc *sc;120uint32_t reg, div;121122sc = clknode_get_softc(clk);123124DEVICE_LOCK(clk);125126READ4(clk, sc->muxdiv_offset, ®);127dprintf("Read: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, reg);128129DEVICE_UNLOCK(clk);130131div = ((reg & sc->div_mask) >> sc->div_shift) + 1;132dprintf("parent_freq=%ju, div=%u\n", *freq, div);133134*freq = *freq / div;135136return (0);137}138139static int140rk_clk_armclk_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,141int flags, int *stop)142{143struct rk_clk_armclk_sc *sc;144struct clknode *p_main;145const char **p_names;146uint64_t best = 0, best_p = 0;147uint32_t div = 0, val = 0;148int err, i, rate = 0;149150sc = clknode_get_softc(clk);151152dprintf("Finding best parent/div for target freq of %ju\n", *fout);153p_names = clknode_get_parent_names(clk);154p_main = clknode_find_by_name(p_names[sc->main_parent]);155156for (i = 0; i < sc->nrates; i++) {157if (sc->rates[i].freq == *fout) {158best = sc->rates[i].freq;159div = sc->rates[i].div;160best_p = best * div;161rate = i;162dprintf("Best parent %s (%d) with best freq at %ju\n",163clknode_get_name(p_main),164sc->main_parent,165best);166break;167}168}169170if (rate == sc->nrates)171return (0);172173if ((flags & CLK_SET_DRYRUN) != 0) {174*fout = best;175*stop = 1;176return (0);177}178179dprintf("Changing parent (%s) freq to %ju\n", clknode_get_name(p_main),180best_p);181err = clknode_set_freq(p_main, best_p, 0, 1);182if (err != 0)183printf("Cannot set %s to %ju\n",184clknode_get_name(p_main),185best_p);186187clknode_set_parent_by_idx(clk, sc->main_parent);188189clknode_get_freq(p_main, &best_p);190dprintf("main parent freq at %ju\n", best_p);191DEVICE_LOCK(clk);192val |= (div - 1) << sc->div_shift;193val |= sc->div_mask << RK_ARMCLK_WRITE_MASK_SHIFT;194dprintf("Write: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, val);195WRITE4(clk, sc->muxdiv_offset, val);196DEVICE_UNLOCK(clk);197198*fout = best;199*stop = 1;200201return (0);202}203204static clknode_method_t rk_clk_armclk_clknode_methods[] = {205/* Device interface */206CLKNODEMETHOD(clknode_init, rk_clk_armclk_init),207CLKNODEMETHOD(clknode_set_mux, rk_clk_armclk_set_mux),208CLKNODEMETHOD(clknode_recalc_freq, rk_clk_armclk_recalc),209CLKNODEMETHOD(clknode_set_freq, rk_clk_armclk_set_freq),210CLKNODEMETHOD_END211};212213DEFINE_CLASS_1(rk_clk_armclk_clknode, rk_clk_armclk_clknode_class,214rk_clk_armclk_clknode_methods, sizeof(struct rk_clk_armclk_sc),215clknode_class);216217int218rk_clk_armclk_register(struct clkdom *clkdom, struct rk_clk_armclk_def *clkdef)219{220struct clknode *clk;221struct rk_clk_armclk_sc *sc;222223clk = clknode_create(clkdom, &rk_clk_armclk_clknode_class,224&clkdef->clkdef);225if (clk == NULL)226return (1);227228sc = clknode_get_softc(clk);229230sc->muxdiv_offset = clkdef->muxdiv_offset;231232sc->mux_shift = clkdef->mux_shift;233sc->mux_width = clkdef->mux_width;234sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;235236sc->div_shift = clkdef->div_shift;237sc->div_width = clkdef->div_width;238sc->div_mask = ((1 << clkdef->div_width) - 1) << sc->div_shift;239240sc->flags = clkdef->flags;241242sc->main_parent = clkdef->main_parent;243sc->alt_parent = clkdef->alt_parent;244245sc->rates = clkdef->rates;246sc->nrates = clkdef->nrates;247248clknode_register(clkdom, clk);249250return (0);251}252253254