177f22241SEmmanuel Vadot /*- 277f22241SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause 377f22241SEmmanuel Vadot * 477f22241SEmmanuel Vadot * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> 577f22241SEmmanuel Vadot * All rights reserved. 677f22241SEmmanuel Vadot * 777f22241SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 877f22241SEmmanuel Vadot * modification, are permitted provided that the following conditions 977f22241SEmmanuel Vadot * are met: 1077f22241SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 1177f22241SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 1277f22241SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 1377f22241SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 1477f22241SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 1577f22241SEmmanuel Vadot * 1677f22241SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1777f22241SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1877f22241SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1977f22241SEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2077f22241SEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2177f22241SEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2277f22241SEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2377f22241SEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2477f22241SEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2577f22241SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2677f22241SEmmanuel Vadot * SUCH DAMAGE. 2777f22241SEmmanuel Vadot */ 2877f22241SEmmanuel Vadot 2977f22241SEmmanuel Vadot #include <sys/param.h> 3077f22241SEmmanuel Vadot #include <sys/conf.h> 3177f22241SEmmanuel Vadot #include <sys/bus.h> 3277f22241SEmmanuel Vadot #include <sys/kernel.h> 3377f22241SEmmanuel Vadot #include <sys/systm.h> 3477f22241SEmmanuel Vadot 3577f22241SEmmanuel Vadot #include <machine/bus.h> 3677f22241SEmmanuel Vadot 37*be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 3877f22241SEmmanuel Vadot #include <dev/extres/syscon/syscon.h> 3977f22241SEmmanuel Vadot 4077f22241SEmmanuel Vadot #include <dev/clk/rockchip/rk_cru.h> 4177f22241SEmmanuel Vadot #include <dev/clk/rockchip/rk_clk_mux.h> 4277f22241SEmmanuel Vadot 4377f22241SEmmanuel Vadot #include "clkdev_if.h" 4477f22241SEmmanuel Vadot #include "syscon_if.h" 4577f22241SEmmanuel Vadot 4677f22241SEmmanuel Vadot #define WR4(_clk, off, val) \ 4777f22241SEmmanuel Vadot CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) 4877f22241SEmmanuel Vadot #define RD4(_clk, off, val) \ 4977f22241SEmmanuel Vadot CLKDEV_READ_4(clknode_get_device(_clk), off, val) 5077f22241SEmmanuel Vadot #define MD4(_clk, off, clr, set ) \ 5177f22241SEmmanuel Vadot CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) 5277f22241SEmmanuel Vadot #define DEVICE_LOCK(_clk) \ 5377f22241SEmmanuel Vadot CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) 5477f22241SEmmanuel Vadot #define DEVICE_UNLOCK(_clk) \ 5577f22241SEmmanuel Vadot CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) 5677f22241SEmmanuel Vadot 5777f22241SEmmanuel Vadot #if 0 5877f22241SEmmanuel Vadot #define dprintf(format, arg...) \ 5977f22241SEmmanuel Vadot printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) 6077f22241SEmmanuel Vadot #else 6177f22241SEmmanuel Vadot #define dprintf(format, arg...) 6277f22241SEmmanuel Vadot #endif 6377f22241SEmmanuel Vadot 6477f22241SEmmanuel Vadot static int rk_clk_mux_init(struct clknode *clk, device_t dev); 6577f22241SEmmanuel Vadot static int rk_clk_mux_set_mux(struct clknode *clk, int idx); 6677f22241SEmmanuel Vadot static int rk_clk_mux_set_freq(struct clknode *clk, uint64_t fparent, 6777f22241SEmmanuel Vadot uint64_t *fout, int flags, int *stop); 6877f22241SEmmanuel Vadot 6977f22241SEmmanuel Vadot struct rk_clk_mux_sc { 7077f22241SEmmanuel Vadot uint32_t offset; 7177f22241SEmmanuel Vadot uint32_t shift; 7277f22241SEmmanuel Vadot uint32_t mask; 7377f22241SEmmanuel Vadot int mux_flags; 7477f22241SEmmanuel Vadot struct syscon *grf; 7577f22241SEmmanuel Vadot }; 7677f22241SEmmanuel Vadot 7777f22241SEmmanuel Vadot static clknode_method_t rk_clk_mux_methods[] = { 7877f22241SEmmanuel Vadot /* Device interface */ 7977f22241SEmmanuel Vadot CLKNODEMETHOD(clknode_init, rk_clk_mux_init), 8077f22241SEmmanuel Vadot CLKNODEMETHOD(clknode_set_mux, rk_clk_mux_set_mux), 8177f22241SEmmanuel Vadot CLKNODEMETHOD(clknode_set_freq, rk_clk_mux_set_freq), 8277f22241SEmmanuel Vadot CLKNODEMETHOD_END 8377f22241SEmmanuel Vadot }; 8477f22241SEmmanuel Vadot DEFINE_CLASS_1(rk_clk_mux, rk_clk_mux_class, rk_clk_mux_methods, 8577f22241SEmmanuel Vadot sizeof(struct rk_clk_mux_sc), clknode_class); 8677f22241SEmmanuel Vadot 8777f22241SEmmanuel Vadot static struct syscon * 8877f22241SEmmanuel Vadot rk_clk_mux_get_grf(struct clknode *clk) 8977f22241SEmmanuel Vadot { 9077f22241SEmmanuel Vadot device_t dev; 9177f22241SEmmanuel Vadot phandle_t node; 9277f22241SEmmanuel Vadot struct syscon *grf; 9377f22241SEmmanuel Vadot 9477f22241SEmmanuel Vadot grf = NULL; 9577f22241SEmmanuel Vadot dev = clknode_get_device(clk); 9677f22241SEmmanuel Vadot node = ofw_bus_get_node(dev); 9777f22241SEmmanuel Vadot if (OF_hasprop(node, "rockchip,grf") && 9877f22241SEmmanuel Vadot syscon_get_by_ofw_property(dev, node, 9977f22241SEmmanuel Vadot "rockchip,grf", &grf) != 0) { 10077f22241SEmmanuel Vadot return (NULL); 10177f22241SEmmanuel Vadot } 10277f22241SEmmanuel Vadot 10377f22241SEmmanuel Vadot return (grf); 10477f22241SEmmanuel Vadot } 10577f22241SEmmanuel Vadot 10677f22241SEmmanuel Vadot static int 10777f22241SEmmanuel Vadot rk_clk_mux_init(struct clknode *clk, device_t dev) 10877f22241SEmmanuel Vadot { 10977f22241SEmmanuel Vadot uint32_t reg; 11077f22241SEmmanuel Vadot struct rk_clk_mux_sc *sc; 11177f22241SEmmanuel Vadot int rv; 11277f22241SEmmanuel Vadot 11377f22241SEmmanuel Vadot sc = clknode_get_softc(clk); 11477f22241SEmmanuel Vadot 11577f22241SEmmanuel Vadot if ((sc->mux_flags & RK_CLK_MUX_GRF) != 0) { 11677f22241SEmmanuel Vadot sc->grf = rk_clk_mux_get_grf(clk); 11777f22241SEmmanuel Vadot if (sc->grf == NULL) 11877f22241SEmmanuel Vadot panic("clock %s has GRF flag set but no syscon is available", 11977f22241SEmmanuel Vadot clknode_get_name(clk)); 12077f22241SEmmanuel Vadot } 12177f22241SEmmanuel Vadot 12277f22241SEmmanuel Vadot DEVICE_LOCK(clk); 12377f22241SEmmanuel Vadot if (sc->grf) { 12477f22241SEmmanuel Vadot reg = SYSCON_READ_4(sc->grf, sc->offset); 12577f22241SEmmanuel Vadot rv = 0; 12677f22241SEmmanuel Vadot } else 12777f22241SEmmanuel Vadot rv = RD4(clk, sc->offset, ®); 12877f22241SEmmanuel Vadot DEVICE_UNLOCK(clk); 12977f22241SEmmanuel Vadot if (rv != 0) { 13077f22241SEmmanuel Vadot return (rv); 13177f22241SEmmanuel Vadot } 13277f22241SEmmanuel Vadot reg = (reg >> sc->shift) & sc->mask; 13377f22241SEmmanuel Vadot clknode_init_parent_idx(clk, reg); 13477f22241SEmmanuel Vadot return(0); 13577f22241SEmmanuel Vadot } 13677f22241SEmmanuel Vadot 13777f22241SEmmanuel Vadot static int 13877f22241SEmmanuel Vadot rk_clk_mux_set_mux(struct clknode *clk, int idx) 13977f22241SEmmanuel Vadot { 14077f22241SEmmanuel Vadot uint32_t reg; 14177f22241SEmmanuel Vadot struct rk_clk_mux_sc *sc; 14277f22241SEmmanuel Vadot int rv; 14377f22241SEmmanuel Vadot 14477f22241SEmmanuel Vadot sc = clknode_get_softc(clk); 14577f22241SEmmanuel Vadot 14677f22241SEmmanuel Vadot DEVICE_LOCK(clk); 14777f22241SEmmanuel Vadot if (sc->grf) 14877f22241SEmmanuel Vadot rv = SYSCON_MODIFY_4(sc->grf, sc->offset, sc->mask << sc->shift, 14977f22241SEmmanuel Vadot ((idx & sc->mask) << sc->shift) | RK_CLK_MUX_MASK); 15077f22241SEmmanuel Vadot else 15177f22241SEmmanuel Vadot rv = MD4(clk, sc->offset, sc->mask << sc->shift, 15277f22241SEmmanuel Vadot ((idx & sc->mask) << sc->shift) | RK_CLK_MUX_MASK); 15377f22241SEmmanuel Vadot if (rv != 0) { 15477f22241SEmmanuel Vadot DEVICE_UNLOCK(clk); 15577f22241SEmmanuel Vadot return (rv); 15677f22241SEmmanuel Vadot } 15777f22241SEmmanuel Vadot if (sc->grf == NULL) 15877f22241SEmmanuel Vadot RD4(clk, sc->offset, ®); 15977f22241SEmmanuel Vadot DEVICE_UNLOCK(clk); 16077f22241SEmmanuel Vadot 16177f22241SEmmanuel Vadot return(0); 16277f22241SEmmanuel Vadot } 16377f22241SEmmanuel Vadot 16477f22241SEmmanuel Vadot static int 16577f22241SEmmanuel Vadot rk_clk_mux_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, 16677f22241SEmmanuel Vadot int flags, int *stop) 16777f22241SEmmanuel Vadot { 16877f22241SEmmanuel Vadot struct rk_clk_mux_sc *sc; 16977f22241SEmmanuel Vadot struct clknode *p_clk, *p_best_clk; 17077f22241SEmmanuel Vadot const char **p_names; 17177f22241SEmmanuel Vadot int p_idx, best_parent; 17277f22241SEmmanuel Vadot int rv; 17377f22241SEmmanuel Vadot 17477f22241SEmmanuel Vadot sc = clknode_get_softc(clk); 17577f22241SEmmanuel Vadot 17677f22241SEmmanuel Vadot if ((sc->mux_flags & RK_CLK_MUX_GRF) != 0) { 17777f22241SEmmanuel Vadot *stop = 1; 17877f22241SEmmanuel Vadot return (ENOTSUP); 17977f22241SEmmanuel Vadot } 18077f22241SEmmanuel Vadot if ((sc->mux_flags & RK_CLK_MUX_REPARENT) == 0) { 18177f22241SEmmanuel Vadot *stop = 0; 18277f22241SEmmanuel Vadot return (0); 18377f22241SEmmanuel Vadot } 18477f22241SEmmanuel Vadot 18577f22241SEmmanuel Vadot dprintf("Finding best parent for target freq of %ju\n", *fout); 18677f22241SEmmanuel Vadot p_names = clknode_get_parent_names(clk); 18777f22241SEmmanuel Vadot for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) { 18877f22241SEmmanuel Vadot p_clk = clknode_find_by_name(p_names[p_idx]); 18977f22241SEmmanuel Vadot dprintf("Testing with parent %s (%d)\n", 19077f22241SEmmanuel Vadot clknode_get_name(p_clk), p_idx); 19177f22241SEmmanuel Vadot 19277f22241SEmmanuel Vadot rv = clknode_set_freq(p_clk, *fout, flags | CLK_SET_DRYRUN, 0); 19377f22241SEmmanuel Vadot dprintf("Testing with parent %s (%d) rv=%d\n", 19477f22241SEmmanuel Vadot clknode_get_name(p_clk), p_idx, rv); 19577f22241SEmmanuel Vadot if (rv == 0) { 19677f22241SEmmanuel Vadot best_parent = p_idx; 19777f22241SEmmanuel Vadot p_best_clk = p_clk; 19877f22241SEmmanuel Vadot *stop = 1; 19977f22241SEmmanuel Vadot } 20077f22241SEmmanuel Vadot } 20177f22241SEmmanuel Vadot 20277f22241SEmmanuel Vadot if (!*stop) 20377f22241SEmmanuel Vadot return (0); 20477f22241SEmmanuel Vadot 20577f22241SEmmanuel Vadot if ((flags & CLK_SET_DRYRUN) != 0) 20677f22241SEmmanuel Vadot return (0); 20777f22241SEmmanuel Vadot 20877f22241SEmmanuel Vadot p_idx = clknode_get_parent_idx(clk); 20977f22241SEmmanuel Vadot if (p_idx != best_parent) { 21077f22241SEmmanuel Vadot dprintf("Switching parent index from %d to %d\n", p_idx, 21177f22241SEmmanuel Vadot best_parent); 21277f22241SEmmanuel Vadot clknode_set_parent_by_idx(clk, best_parent); 21377f22241SEmmanuel Vadot } 21477f22241SEmmanuel Vadot 21577f22241SEmmanuel Vadot clknode_set_freq(p_best_clk, *fout, flags, 0); 21677f22241SEmmanuel Vadot clknode_get_freq(p_best_clk, fout); 21777f22241SEmmanuel Vadot 21877f22241SEmmanuel Vadot return (0); 21977f22241SEmmanuel Vadot } 22077f22241SEmmanuel Vadot 22177f22241SEmmanuel Vadot int 22277f22241SEmmanuel Vadot rk_clk_mux_register(struct clkdom *clkdom, struct rk_clk_mux_def *clkdef) 22377f22241SEmmanuel Vadot { 22477f22241SEmmanuel Vadot struct clknode *clk; 22577f22241SEmmanuel Vadot struct rk_clk_mux_sc *sc; 22677f22241SEmmanuel Vadot 22777f22241SEmmanuel Vadot clk = clknode_create(clkdom, &rk_clk_mux_class, &clkdef->clkdef); 22877f22241SEmmanuel Vadot if (clk == NULL) 22977f22241SEmmanuel Vadot return (1); 23077f22241SEmmanuel Vadot 23177f22241SEmmanuel Vadot sc = clknode_get_softc(clk); 23277f22241SEmmanuel Vadot sc->offset = clkdef->offset; 23377f22241SEmmanuel Vadot sc->shift = clkdef->shift; 23477f22241SEmmanuel Vadot sc->mask = (1 << clkdef->width) - 1; 23577f22241SEmmanuel Vadot sc->mux_flags = clkdef->mux_flags; 23677f22241SEmmanuel Vadot 23777f22241SEmmanuel Vadot clknode_register(clkdom, clk); 23877f22241SEmmanuel Vadot return (0); 23977f22241SEmmanuel Vadot } 240