Path: blob/main/sys/dev/clk/rockchip/rk_clk_mux.c
107563 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright 2016 Michal Meloun <[email protected]>4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/conf.h>30#include <sys/bus.h>31#include <sys/kernel.h>32#include <sys/systm.h>3334#include <machine/bus.h>3536#include <dev/clk/clk.h>37#include <dev/syscon/syscon.h>3839#include <dev/clk/rockchip/rk_cru.h>40#include <dev/clk/rockchip/rk_clk_mux.h>4142#include "clkdev_if.h"43#include "syscon_if.h"4445#define WR4(_clk, off, val) \46CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)47#define RD4(_clk, off, val) \48CLKDEV_READ_4(clknode_get_device(_clk), off, val)49#define MD4(_clk, off, clr, set ) \50CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)51#define DEVICE_LOCK(_clk) \52CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))53#define DEVICE_UNLOCK(_clk) \54CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))5556#if 057#define dprintf(format, arg...) \58printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)59#else60#define dprintf(format, arg...)61#endif6263static int rk_clk_mux_init(struct clknode *clk, device_t dev);64static int rk_clk_mux_set_mux(struct clknode *clk, int idx);65static int rk_clk_mux_set_freq(struct clknode *clk, uint64_t fparent,66uint64_t *fout, int flags, int *stop);6768struct rk_clk_mux_sc {69uint32_t offset;70uint32_t shift;71uint32_t mask;72int mux_flags;73struct syscon *grf;74};7576static clknode_method_t rk_clk_mux_methods[] = {77/* Device interface */78CLKNODEMETHOD(clknode_init, rk_clk_mux_init),79CLKNODEMETHOD(clknode_set_mux, rk_clk_mux_set_mux),80CLKNODEMETHOD(clknode_set_freq, rk_clk_mux_set_freq),81CLKNODEMETHOD_END82};83DEFINE_CLASS_1(rk_clk_mux, rk_clk_mux_class, rk_clk_mux_methods,84sizeof(struct rk_clk_mux_sc), clknode_class);8586static struct syscon *87rk_clk_mux_get_grf(struct clknode *clk)88{89device_t dev;90phandle_t node;91struct syscon *grf;9293grf = NULL;94dev = clknode_get_device(clk);95node = ofw_bus_get_node(dev);96if (OF_hasprop(node, "rockchip,grf") &&97syscon_get_by_ofw_property(dev, node,98"rockchip,grf", &grf) != 0) {99return (NULL);100}101102return (grf);103}104105static int106rk_clk_mux_init(struct clknode *clk, device_t dev)107{108uint32_t reg;109struct rk_clk_mux_sc *sc;110int rv;111112sc = clknode_get_softc(clk);113114if ((sc->mux_flags & RK_CLK_MUX_GRF) != 0) {115sc->grf = rk_clk_mux_get_grf(clk);116if (sc->grf == NULL)117panic("clock %s has GRF flag set but no syscon is available",118clknode_get_name(clk));119}120121DEVICE_LOCK(clk);122if (sc->grf) {123reg = SYSCON_READ_4(sc->grf, sc->offset);124rv = 0;125} else126rv = RD4(clk, sc->offset, ®);127DEVICE_UNLOCK(clk);128if (rv != 0) {129return (rv);130}131reg = (reg >> sc->shift) & sc->mask;132clknode_init_parent_idx(clk, reg);133return(0);134}135136static int137rk_clk_mux_set_mux(struct clknode *clk, int idx)138{139uint32_t reg;140struct rk_clk_mux_sc *sc;141int rv;142143sc = clknode_get_softc(clk);144145DEVICE_LOCK(clk);146if (sc->grf)147rv = SYSCON_MODIFY_4(sc->grf, sc->offset, sc->mask << sc->shift,148((idx & sc->mask) << sc->shift) | RK_CLK_MUX_MASK);149else150rv = MD4(clk, sc->offset, sc->mask << sc->shift,151((idx & sc->mask) << sc->shift) | RK_CLK_MUX_MASK);152if (rv != 0) {153DEVICE_UNLOCK(clk);154return (rv);155}156if (sc->grf == NULL)157RD4(clk, sc->offset, ®);158DEVICE_UNLOCK(clk);159160return(0);161}162163static int164rk_clk_mux_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,165int flags, int *stop)166{167struct rk_clk_mux_sc *sc;168struct clknode *p_clk, *p_best_clk;169const char **p_names;170int p_idx, best_parent;171int rv;172173sc = clknode_get_softc(clk);174175if ((sc->mux_flags & RK_CLK_MUX_GRF) != 0) {176*stop = 1;177return (ENOTSUP);178}179if ((sc->mux_flags & RK_CLK_MUX_REPARENT) == 0) {180*stop = 0;181return (0);182}183184dprintf("Finding best parent for target freq of %ju\n", *fout);185p_names = clknode_get_parent_names(clk);186for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {187p_clk = clknode_find_by_name(p_names[p_idx]);188dprintf("Testing with parent %s (%d)\n",189clknode_get_name(p_clk), p_idx);190191rv = clknode_set_freq(p_clk, *fout, flags | CLK_SET_DRYRUN, 0);192dprintf("Testing with parent %s (%d) rv=%d\n",193clknode_get_name(p_clk), p_idx, rv);194if (rv == 0) {195best_parent = p_idx;196p_best_clk = p_clk;197*stop = 1;198}199}200201if (!*stop)202return (0);203204if ((flags & CLK_SET_DRYRUN) != 0)205return (0);206207p_idx = clknode_get_parent_idx(clk);208if (p_idx != best_parent) {209dprintf("Switching parent index from %d to %d\n", p_idx,210best_parent);211clknode_set_parent_by_idx(clk, best_parent);212}213214clknode_set_freq(p_best_clk, *fout, flags, 0);215clknode_get_freq(p_best_clk, fout);216217return (0);218}219220int221rk_clk_mux_register(struct clkdom *clkdom, struct rk_clk_mux_def *clkdef)222{223struct clknode *clk;224struct rk_clk_mux_sc *sc;225226clk = clknode_create(clkdom, &rk_clk_mux_class, &clkdef->clkdef);227if (clk == NULL)228return (1);229230sc = clknode_get_softc(clk);231sc->offset = clkdef->offset;232sc->shift = clkdef->shift;233sc->mask = (1 << clkdef->width) - 1;234sc->mux_flags = clkdef->mux_flags;235236clknode_register(clkdom, clk);237return (0);238}239240241