177f22241SEmmanuel Vadot /*-
277f22241SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause
377f22241SEmmanuel Vadot *
477f22241SEmmanuel Vadot * Copyright (c) 2018 Emmanuel Vadot <manu@freebsd.org>
577f22241SEmmanuel Vadot *
677f22241SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without
777f22241SEmmanuel Vadot * modification, are permitted provided that the following conditions
877f22241SEmmanuel Vadot * are met:
977f22241SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright
1077f22241SEmmanuel Vadot * notice, this list of conditions and the following disclaimer.
1177f22241SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright
1277f22241SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the
1377f22241SEmmanuel Vadot * documentation and/or other materials provided with the distribution.
1477f22241SEmmanuel Vadot *
1577f22241SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1677f22241SEmmanuel Vadot * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1777f22241SEmmanuel Vadot * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1877f22241SEmmanuel Vadot * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1977f22241SEmmanuel Vadot * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2077f22241SEmmanuel Vadot * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2177f22241SEmmanuel Vadot * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2277f22241SEmmanuel Vadot * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2377f22241SEmmanuel Vadot * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2477f22241SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2577f22241SEmmanuel Vadot * SUCH DAMAGE.
2677f22241SEmmanuel Vadot */
2777f22241SEmmanuel Vadot
2877f22241SEmmanuel Vadot #include <sys/param.h>
2977f22241SEmmanuel Vadot #include <sys/systm.h>
3077f22241SEmmanuel Vadot #include <sys/bus.h>
3177f22241SEmmanuel Vadot
32*be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
3377f22241SEmmanuel Vadot
3477f22241SEmmanuel Vadot #include <dev/clk/rockchip/rk_clk_armclk.h>
3577f22241SEmmanuel Vadot
3677f22241SEmmanuel Vadot #include "clkdev_if.h"
3777f22241SEmmanuel Vadot
3877f22241SEmmanuel Vadot struct rk_clk_armclk_sc {
3977f22241SEmmanuel Vadot uint32_t muxdiv_offset;
4077f22241SEmmanuel Vadot uint32_t mux_shift;
4177f22241SEmmanuel Vadot uint32_t mux_width;
4277f22241SEmmanuel Vadot uint32_t mux_mask;
4377f22241SEmmanuel Vadot
4477f22241SEmmanuel Vadot uint32_t div_shift;
4577f22241SEmmanuel Vadot uint32_t div_width;
4677f22241SEmmanuel Vadot uint32_t div_mask;
4777f22241SEmmanuel Vadot
4877f22241SEmmanuel Vadot uint32_t gate_offset;
4977f22241SEmmanuel Vadot uint32_t gate_shift;
5077f22241SEmmanuel Vadot
5177f22241SEmmanuel Vadot uint32_t flags;
5277f22241SEmmanuel Vadot
5377f22241SEmmanuel Vadot uint32_t main_parent;
5477f22241SEmmanuel Vadot uint32_t alt_parent;
5577f22241SEmmanuel Vadot
5677f22241SEmmanuel Vadot struct rk_clk_armclk_rates *rates;
5777f22241SEmmanuel Vadot int nrates;
5877f22241SEmmanuel Vadot };
5977f22241SEmmanuel Vadot
6077f22241SEmmanuel Vadot #define WRITE4(_clk, off, val) \
6177f22241SEmmanuel Vadot CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
6277f22241SEmmanuel Vadot #define READ4(_clk, off, val) \
6377f22241SEmmanuel Vadot CLKDEV_READ_4(clknode_get_device(_clk), off, val)
6477f22241SEmmanuel Vadot #define DEVICE_LOCK(_clk) \
6577f22241SEmmanuel Vadot CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
6677f22241SEmmanuel Vadot #define DEVICE_UNLOCK(_clk) \
6777f22241SEmmanuel Vadot CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
6877f22241SEmmanuel Vadot
6977f22241SEmmanuel Vadot #define RK_ARMCLK_WRITE_MASK_SHIFT 16
7077f22241SEmmanuel Vadot
7177f22241SEmmanuel Vadot #if 0
7277f22241SEmmanuel Vadot #define dprintf(format, arg...) \
7377f22241SEmmanuel Vadot printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg)
7477f22241SEmmanuel Vadot #else
7577f22241SEmmanuel Vadot #define dprintf(format, arg...)
7677f22241SEmmanuel Vadot #endif
7777f22241SEmmanuel Vadot
7877f22241SEmmanuel Vadot static int
rk_clk_armclk_init(struct clknode * clk,device_t dev)7977f22241SEmmanuel Vadot rk_clk_armclk_init(struct clknode *clk, device_t dev)
8077f22241SEmmanuel Vadot {
8177f22241SEmmanuel Vadot struct rk_clk_armclk_sc *sc;
8277f22241SEmmanuel Vadot uint32_t val, idx;
8377f22241SEmmanuel Vadot
8477f22241SEmmanuel Vadot sc = clknode_get_softc(clk);
8577f22241SEmmanuel Vadot
8677f22241SEmmanuel Vadot idx = 0;
8777f22241SEmmanuel Vadot DEVICE_LOCK(clk);
8877f22241SEmmanuel Vadot READ4(clk, sc->muxdiv_offset, &val);
8977f22241SEmmanuel Vadot DEVICE_UNLOCK(clk);
9077f22241SEmmanuel Vadot
9177f22241SEmmanuel Vadot idx = (val & sc->mux_mask) >> sc->mux_shift;
9277f22241SEmmanuel Vadot
9377f22241SEmmanuel Vadot clknode_init_parent_idx(clk, idx);
9477f22241SEmmanuel Vadot
9577f22241SEmmanuel Vadot return (0);
9677f22241SEmmanuel Vadot }
9777f22241SEmmanuel Vadot
9877f22241SEmmanuel Vadot static int
rk_clk_armclk_set_mux(struct clknode * clk,int index)9977f22241SEmmanuel Vadot rk_clk_armclk_set_mux(struct clknode *clk, int index)
10077f22241SEmmanuel Vadot {
10177f22241SEmmanuel Vadot struct rk_clk_armclk_sc *sc;
10277f22241SEmmanuel Vadot uint32_t val = 0;
10377f22241SEmmanuel Vadot
10477f22241SEmmanuel Vadot sc = clknode_get_softc(clk);
10577f22241SEmmanuel Vadot
10677f22241SEmmanuel Vadot dprintf("Set mux to %d\n", index);
10777f22241SEmmanuel Vadot DEVICE_LOCK(clk);
10877f22241SEmmanuel Vadot val |= index << sc->mux_shift;
10977f22241SEmmanuel Vadot val |= sc->mux_mask << RK_ARMCLK_WRITE_MASK_SHIFT;
11077f22241SEmmanuel Vadot dprintf("Write: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, val);
11177f22241SEmmanuel Vadot WRITE4(clk, sc->muxdiv_offset, val);
11277f22241SEmmanuel Vadot DEVICE_UNLOCK(clk);
11377f22241SEmmanuel Vadot
11477f22241SEmmanuel Vadot return (0);
11577f22241SEmmanuel Vadot }
11677f22241SEmmanuel Vadot
11777f22241SEmmanuel Vadot static int
rk_clk_armclk_recalc(struct clknode * clk,uint64_t * freq)11877f22241SEmmanuel Vadot rk_clk_armclk_recalc(struct clknode *clk, uint64_t *freq)
11977f22241SEmmanuel Vadot {
12077f22241SEmmanuel Vadot struct rk_clk_armclk_sc *sc;
12177f22241SEmmanuel Vadot uint32_t reg, div;
12277f22241SEmmanuel Vadot
12377f22241SEmmanuel Vadot sc = clknode_get_softc(clk);
12477f22241SEmmanuel Vadot
12577f22241SEmmanuel Vadot DEVICE_LOCK(clk);
12677f22241SEmmanuel Vadot
12777f22241SEmmanuel Vadot READ4(clk, sc->muxdiv_offset, ®);
12877f22241SEmmanuel Vadot dprintf("Read: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, reg);
12977f22241SEmmanuel Vadot
13077f22241SEmmanuel Vadot DEVICE_UNLOCK(clk);
13177f22241SEmmanuel Vadot
13277f22241SEmmanuel Vadot div = ((reg & sc->div_mask) >> sc->div_shift) + 1;
13377f22241SEmmanuel Vadot dprintf("parent_freq=%ju, div=%u\n", *freq, div);
13477f22241SEmmanuel Vadot
13577f22241SEmmanuel Vadot *freq = *freq / div;
13677f22241SEmmanuel Vadot
13777f22241SEmmanuel Vadot return (0);
13877f22241SEmmanuel Vadot }
13977f22241SEmmanuel Vadot
14077f22241SEmmanuel Vadot static int
rk_clk_armclk_set_freq(struct clknode * clk,uint64_t fparent,uint64_t * fout,int flags,int * stop)14177f22241SEmmanuel Vadot rk_clk_armclk_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
14277f22241SEmmanuel Vadot int flags, int *stop)
14377f22241SEmmanuel Vadot {
14477f22241SEmmanuel Vadot struct rk_clk_armclk_sc *sc;
14577f22241SEmmanuel Vadot struct clknode *p_main;
14677f22241SEmmanuel Vadot const char **p_names;
14777f22241SEmmanuel Vadot uint64_t best = 0, best_p = 0;
14877f22241SEmmanuel Vadot uint32_t div = 0, val = 0;
14977f22241SEmmanuel Vadot int err, i, rate = 0;
15077f22241SEmmanuel Vadot
15177f22241SEmmanuel Vadot sc = clknode_get_softc(clk);
15277f22241SEmmanuel Vadot
15377f22241SEmmanuel Vadot dprintf("Finding best parent/div for target freq of %ju\n", *fout);
15477f22241SEmmanuel Vadot p_names = clknode_get_parent_names(clk);
15577f22241SEmmanuel Vadot p_main = clknode_find_by_name(p_names[sc->main_parent]);
15677f22241SEmmanuel Vadot
15777f22241SEmmanuel Vadot for (i = 0; i < sc->nrates; i++) {
15877f22241SEmmanuel Vadot if (sc->rates[i].freq == *fout) {
15977f22241SEmmanuel Vadot best = sc->rates[i].freq;
16077f22241SEmmanuel Vadot div = sc->rates[i].div;
16177f22241SEmmanuel Vadot best_p = best * div;
16277f22241SEmmanuel Vadot rate = i;
16377f22241SEmmanuel Vadot dprintf("Best parent %s (%d) with best freq at %ju\n",
16477f22241SEmmanuel Vadot clknode_get_name(p_main),
16577f22241SEmmanuel Vadot sc->main_parent,
16677f22241SEmmanuel Vadot best);
16777f22241SEmmanuel Vadot break;
16877f22241SEmmanuel Vadot }
16977f22241SEmmanuel Vadot }
17077f22241SEmmanuel Vadot
17177f22241SEmmanuel Vadot if (rate == sc->nrates)
17277f22241SEmmanuel Vadot return (0);
17377f22241SEmmanuel Vadot
17477f22241SEmmanuel Vadot if ((flags & CLK_SET_DRYRUN) != 0) {
17577f22241SEmmanuel Vadot *fout = best;
17677f22241SEmmanuel Vadot *stop = 1;
17777f22241SEmmanuel Vadot return (0);
17877f22241SEmmanuel Vadot }
17977f22241SEmmanuel Vadot
18077f22241SEmmanuel Vadot dprintf("Changing parent (%s) freq to %ju\n", clknode_get_name(p_main),
18177f22241SEmmanuel Vadot best_p);
18277f22241SEmmanuel Vadot err = clknode_set_freq(p_main, best_p, 0, 1);
18377f22241SEmmanuel Vadot if (err != 0)
18477f22241SEmmanuel Vadot printf("Cannot set %s to %ju\n",
18577f22241SEmmanuel Vadot clknode_get_name(p_main),
18677f22241SEmmanuel Vadot best_p);
18777f22241SEmmanuel Vadot
18877f22241SEmmanuel Vadot clknode_set_parent_by_idx(clk, sc->main_parent);
18977f22241SEmmanuel Vadot
19077f22241SEmmanuel Vadot clknode_get_freq(p_main, &best_p);
19177f22241SEmmanuel Vadot dprintf("main parent freq at %ju\n", best_p);
19277f22241SEmmanuel Vadot DEVICE_LOCK(clk);
19377f22241SEmmanuel Vadot val |= (div - 1) << sc->div_shift;
19477f22241SEmmanuel Vadot val |= sc->div_mask << RK_ARMCLK_WRITE_MASK_SHIFT;
19577f22241SEmmanuel Vadot dprintf("Write: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, val);
19677f22241SEmmanuel Vadot WRITE4(clk, sc->muxdiv_offset, val);
19777f22241SEmmanuel Vadot DEVICE_UNLOCK(clk);
19877f22241SEmmanuel Vadot
19977f22241SEmmanuel Vadot *fout = best;
20077f22241SEmmanuel Vadot *stop = 1;
20177f22241SEmmanuel Vadot
20277f22241SEmmanuel Vadot return (0);
20377f22241SEmmanuel Vadot }
20477f22241SEmmanuel Vadot
20577f22241SEmmanuel Vadot static clknode_method_t rk_clk_armclk_clknode_methods[] = {
20677f22241SEmmanuel Vadot /* Device interface */
20777f22241SEmmanuel Vadot CLKNODEMETHOD(clknode_init, rk_clk_armclk_init),
20877f22241SEmmanuel Vadot CLKNODEMETHOD(clknode_set_mux, rk_clk_armclk_set_mux),
20977f22241SEmmanuel Vadot CLKNODEMETHOD(clknode_recalc_freq, rk_clk_armclk_recalc),
21077f22241SEmmanuel Vadot CLKNODEMETHOD(clknode_set_freq, rk_clk_armclk_set_freq),
21177f22241SEmmanuel Vadot CLKNODEMETHOD_END
21277f22241SEmmanuel Vadot };
21377f22241SEmmanuel Vadot
21477f22241SEmmanuel Vadot DEFINE_CLASS_1(rk_clk_armclk_clknode, rk_clk_armclk_clknode_class,
21577f22241SEmmanuel Vadot rk_clk_armclk_clknode_methods, sizeof(struct rk_clk_armclk_sc),
21677f22241SEmmanuel Vadot clknode_class);
21777f22241SEmmanuel Vadot
21877f22241SEmmanuel Vadot int
rk_clk_armclk_register(struct clkdom * clkdom,struct rk_clk_armclk_def * clkdef)21977f22241SEmmanuel Vadot rk_clk_armclk_register(struct clkdom *clkdom, struct rk_clk_armclk_def *clkdef)
22077f22241SEmmanuel Vadot {
22177f22241SEmmanuel Vadot struct clknode *clk;
22277f22241SEmmanuel Vadot struct rk_clk_armclk_sc *sc;
22377f22241SEmmanuel Vadot
22477f22241SEmmanuel Vadot clk = clknode_create(clkdom, &rk_clk_armclk_clknode_class,
22577f22241SEmmanuel Vadot &clkdef->clkdef);
22677f22241SEmmanuel Vadot if (clk == NULL)
22777f22241SEmmanuel Vadot return (1);
22877f22241SEmmanuel Vadot
22977f22241SEmmanuel Vadot sc = clknode_get_softc(clk);
23077f22241SEmmanuel Vadot
23177f22241SEmmanuel Vadot sc->muxdiv_offset = clkdef->muxdiv_offset;
23277f22241SEmmanuel Vadot
23377f22241SEmmanuel Vadot sc->mux_shift = clkdef->mux_shift;
23477f22241SEmmanuel Vadot sc->mux_width = clkdef->mux_width;
23577f22241SEmmanuel Vadot sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
23677f22241SEmmanuel Vadot
23777f22241SEmmanuel Vadot sc->div_shift = clkdef->div_shift;
23877f22241SEmmanuel Vadot sc->div_width = clkdef->div_width;
23977f22241SEmmanuel Vadot sc->div_mask = ((1 << clkdef->div_width) - 1) << sc->div_shift;
24077f22241SEmmanuel Vadot
24177f22241SEmmanuel Vadot sc->flags = clkdef->flags;
24277f22241SEmmanuel Vadot
24377f22241SEmmanuel Vadot sc->main_parent = clkdef->main_parent;
24477f22241SEmmanuel Vadot sc->alt_parent = clkdef->alt_parent;
24577f22241SEmmanuel Vadot
24677f22241SEmmanuel Vadot sc->rates = clkdef->rates;
24777f22241SEmmanuel Vadot sc->nrates = clkdef->nrates;
24877f22241SEmmanuel Vadot
24977f22241SEmmanuel Vadot clknode_register(clkdom, clk);
25077f22241SEmmanuel Vadot
25177f22241SEmmanuel Vadot return (0);
25277f22241SEmmanuel Vadot }
253