diff --git a/sys/arm64/rockchip/clk/rk3328_cru.c b/sys/arm64/rockchip/clk/rk3328_cru.c index c5b2985a7d4..2063ff0c629 100644 --- a/sys/arm64/rockchip/clk/rk3328_cru.c +++ b/sys/arm64/rockchip/clk/rk3328_cru.c @@ -593,9 +593,60 @@ static struct rk_clk_composite_def aclk_bus_pre = { .flags = RK_CLK_COMPOSITE_HAVE_MUX | RK_CLK_COMPOSITE_HAVE_GATE, }; +static struct rk_clk_armclk_rates rk3328_armclk_rates[] = { + { + .freq = 1296000000, + .div = 1, + }, + { + .freq = 1200000000, + .div = 1, + }, + { + .freq = 1104000000, + .div = 1, + }, + { + .freq = 1008000000, + .div = 1, + }, + { + .freq = 912000000, + .div = 1, + }, + { + .freq = 816000000, + .div = 1, + }, + { + .freq = 696000000, + .div = 1, + }, + { + .freq = 600000000, + .div = 1, + }, + { + .freq = 408000000, + .div = 1, + }, + { + .freq = 312000000, + .div = 1, + }, + { + .freq = 216000000, + .div = 1, + }, + { + .freq = 96000000, + .div = 1, + }, +}; + #define ARMCLK 6 static const char *armclk_parents[] = {"apll", "gpll", "dpll", "npll" }; -static struct rk_clk_composite_def armclk = { +static struct rk_clk_armclk_def armclk = { .clkdef = { .id = ARMCLK, .name = "armclk", @@ -610,6 +661,11 @@ static struct rk_clk_composite_def armclk = { .div_width = 5, .flags = RK_CLK_COMPOSITE_HAVE_MUX, + .main_parent = 3, /* npll */ + .alt_parent = 0, /* apll */ + + .rates = rk3328_armclk_rates, + .nrates = nitems(rk3328_armclk_rates), }; /* CRU_CLKSEL_CON1 */ @@ -823,10 +879,6 @@ static struct rk_clk rk3328_clks[] = { .type = RK_CLK_COMPOSITE, .clk.composite = &aclk_bus_pre }, - { - .type = RK_CLK_COMPOSITE, - .clk.composite = &armclk - }, { .type = RK_CLK_COMPOSITE, .clk.composite = &hclk_bus_pre @@ -836,6 +888,11 @@ static struct rk_clk rk3328_clks[] = { .clk.composite = &pclk_bus_pre }, + { + .type = RK_CLK_ARMCLK, + .clk.armclk = &armclk, + }, + { .type = RK_CLK_COMPOSITE, .clk.composite = &aclk_peri_pre, diff --git a/sys/arm64/rockchip/clk/rk_clk_armclk.c b/sys/arm64/rockchip/clk/rk_clk_armclk.c new file mode 100644 index 00000000000..57a3141edff --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_armclk.c @@ -0,0 +1,237 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" + +struct rk_clk_armclk_sc { + uint32_t muxdiv_offset; + uint32_t mux_shift; + uint32_t mux_width; + uint32_t mux_mask; + + uint32_t div_shift; + uint32_t div_width; + uint32_t div_mask; + + uint32_t gate_offset; + uint32_t gate_shift; + + uint32_t flags; + + uint32_t main_parent; + uint32_t alt_parent; + + struct rk_clk_armclk_rates *rates; + int nrates; +}; + +#define WRITE4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define READ4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +#define RK_ARMCLK_WRITE_MASK 0xFFFF0000 + +static int +rk_clk_armclk_init(struct clknode *clk, device_t dev) +{ + struct rk_clk_armclk_sc *sc; + uint32_t val, idx; + + sc = clknode_get_softc(clk); + + idx = 0; + DEVICE_LOCK(clk); + READ4(clk, sc->muxdiv_offset, &val); + DEVICE_UNLOCK(clk); + + idx = (val & sc->mux_mask) >> sc->mux_shift; + + clknode_init_parent_idx(clk, idx); + + return (0); +} + +static int +rk_clk_armclk_set_mux(struct clknode *clk, int index) +{ + struct rk_clk_armclk_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->muxdiv_offset, &val); + val &= ~(sc->mux_mask >> sc->mux_shift); + val |= index << sc->mux_shift; + WRITE4(clk, sc->muxdiv_offset, val); + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +rk_clk_armclk_recalc(struct clknode *clk, uint64_t *freq) +{ + struct rk_clk_armclk_sc *sc; + uint32_t reg, div; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + + READ4(clk, sc->muxdiv_offset, ®); + + DEVICE_UNLOCK(clk); + + div = ((reg & sc->div_mask) >> sc->div_shift) + 1; + + *freq = *freq / div; + + return (0); +} + +static int +rk_clk_armclk_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct rk_clk_armclk_sc *sc; + struct clknode *p_main; + const char **p_names; + uint64_t best = 0, best_p = 0; + uint32_t div = 0, val; + int err, i, rate = 0; + + sc = clknode_get_softc(clk); + + p_names = clknode_get_parent_names(clk); + p_main = clknode_find_by_name(p_names[sc->main_parent]); + clknode_set_parent_by_idx(clk, sc->main_parent); + + for (i = 0; i < sc->nrates; i++) { + if (sc->rates[i].freq == *fout) { + best = sc->rates[i].freq; + div = sc->rates[i].div; + best_p = best * (div + 1); + rate = i; + } + } + + if (rate == 0) + return (0); + + err = clknode_set_freq(p_main, best_p, 0, 1); + if (err != 0) + printf("Cannot set %s to %lu\n", + clknode_get_name(p_main), + best_p); + + if ((flags & CLK_SET_DRYRUN) != 0) { + *fout = best; + *stop = 1; + return (0); + } + + DEVICE_LOCK(clk); + READ4(clk, sc->muxdiv_offset, &val); + val &= ~sc->div_mask; + val |= div << sc->div_shift; + WRITE4(clk, sc->muxdiv_offset, val | RK_CLK_ARMCLK_MASK); + DEVICE_UNLOCK(clk); + + *fout = best; + *stop = 1; + + return (0); +} + +static clknode_method_t rk_clk_armclk_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, rk_clk_armclk_init), + CLKNODEMETHOD(clknode_set_mux, rk_clk_armclk_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, rk_clk_armclk_recalc), + CLKNODEMETHOD(clknode_set_freq, rk_clk_armclk_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(rk_clk_armclk_clknode, rk_clk_armclk_clknode_class, + rk_clk_armclk_clknode_methods, sizeof(struct rk_clk_armclk_sc), + clknode_class); + +int +rk_clk_armclk_register(struct clkdom *clkdom, struct rk_clk_armclk_def *clkdef) +{ + struct clknode *clk; + struct rk_clk_armclk_sc *sc; + + clk = clknode_create(clkdom, &rk_clk_armclk_clknode_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + + sc->muxdiv_offset = clkdef->muxdiv_offset; + + sc->mux_shift = clkdef->mux_shift; + sc->mux_width = clkdef->mux_width; + sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift; + + sc->div_shift = clkdef->div_shift; + sc->div_width = clkdef->div_width; + sc->div_mask = ((1 << clkdef->div_width) - 1) << sc->div_shift; + + sc->flags = clkdef->flags; + + sc->main_parent = clkdef->main_parent; + sc->alt_parent = clkdef->alt_parent; + + sc->rates = clkdef->rates; + sc->nrates = clkdef->nrates; + + clknode_register(clkdom, clk); + + return (0); +} diff --git a/sys/arm64/rockchip/clk/rk_clk_armclk.h b/sys/arm64/rockchip/clk/rk_clk_armclk.h new file mode 100644 index 00000000000..b71d48066b5 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_armclk.h @@ -0,0 +1,66 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2018 Emmanuel Vadot + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _RK_CLK_ARMCLK_H_ +#define _RK_CLK_ARMCLK_H_ + +#include + +struct rk_clk_armclk_rates { + uint64_t freq; + uint32_t div; +}; + +struct rk_clk_armclk_def { + struct clknode_init_def clkdef; + + uint32_t muxdiv_offset; + + uint32_t mux_shift; + uint32_t mux_width; + + uint32_t div_shift; + uint32_t div_width; + + uint32_t flags; + + uint32_t main_parent; + uint32_t alt_parent; + + struct rk_clk_armclk_rates *rates; + int nrates; +}; + +#define RK_CLK_ARMCLK_MASK 0xFFFF0000 + +int rk_clk_armclk_register(struct clkdom *clkdom, + struct rk_clk_armclk_def *clkdef); + +#endif /* _RK_CLK_ARMCLK_H_ */ diff --git a/sys/arm64/rockchip/clk/rk_cru.c b/sys/arm64/rockchip/clk/rk_cru.c index 22f798449de..d4a8c1ddc9b 100644 --- a/sys/arm64/rockchip/clk/rk_cru.c +++ b/sys/arm64/rockchip/clk/rk_cru.c @@ -230,6 +230,9 @@ rk_cru_attach(device_t dev) case RK_CLK_MUX: rk_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux); break; + case RK_CLK_ARMCLK: + rk_clk_armclk_register(sc->clkdom, sc->clks[i].clk.armclk); + break; default: device_printf(dev, "Unknown clock type\n"); return (ENXIO); diff --git a/sys/arm64/rockchip/clk/rk_cru.h b/sys/arm64/rockchip/clk/rk_cru.h index 9541cda63d7..e9326ab415e 100644 --- a/sys/arm64/rockchip/clk/rk_cru.h +++ b/sys/arm64/rockchip/clk/rk_cru.h @@ -31,6 +31,7 @@ #ifndef __RK_CRU_H__ #define __RK_CRU_H__ +#include #include #include #include @@ -63,6 +64,7 @@ enum rk_clk_type { RK_CLK_PLL, RK_CLK_COMPOSITE, RK_CLK_MUX, + RK_CLK_ARMCLK, }; struct rk_clk { @@ -71,6 +73,7 @@ struct rk_clk { struct rk_clk_pll_def *pll; struct rk_clk_composite_def *composite; struct rk_clk_mux_def *mux; + struct rk_clk_armclk_def *armclk; } clk; }; @@ -86,6 +89,9 @@ struct rk_cru_softc { int ngates; struct rk_clk *clks; int nclks; + struct rk_clk_armclk_def *armclk; + struct rk_clk_armclk_rates *armclk_rates; + int narmclk_rates; }; DECLARE_CLASS(rk_cru_driver); diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index abfc066ff9b..67bba7ab5f6 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -248,6 +248,7 @@ arm64/rockchip/rk_grf.c optional fdt soc_rockchip_rk3328 arm64/rockchip/rk_pinctrl.c optional fdt soc_rockchip_rk3328 arm64/rockchip/rk_gpio.c optional fdt soc_rockchip_rk3328 arm64/rockchip/clk/rk_cru.c optional fdt soc_rockchip_rk3328 +arm64/rockchip/clk/rk_clk_armclk.c optional fdt soc_rockchip_rk3328 arm64/rockchip/clk/rk_clk_composite.c optional fdt soc_rockchip_rk3328 arm64/rockchip/clk/rk_clk_gate.c optional fdt soc_rockchip_rk3328 arm64/rockchip/clk/rk_clk_mux.c optional fdt soc_rockchip_rk3328