xref: /freebsd/sys/dev/clk/rockchip/rk_clk_armclk.c (revision be82b3a0bf72ed3b5f01ac9fcd8dcd3802e3c742)
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, &reg);
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