diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC index 3cd4c19b2e0b..c13051f15f07 100644 --- a/sys/arm64/conf/GENERIC +++ b/sys/arm64/conf/GENERIC @@ -97,6 +97,7 @@ options SOC_ALLWINNER_H5 options SOC_CAVM_THUNDERX options SOC_HISI_HI6220 options SOC_BRCM_BCM2837 +options SOC_ROCKCHIP_RK3328 # Annapurna Alpine drivers device al_ccu # Alpine Cache Coherency Unit diff --git a/sys/arm64/rockchip/clk/rk3328_cru.c b/sys/arm64/rockchip/clk/rk3328_cru.c new file mode 100644 index 000000000000..1fde74bd10b5 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk3328_cru.c @@ -0,0 +1,476 @@ +/*- + * 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 +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +/* GATES */ + +#define ACLK_PERI 153 +#define HCLK_SDMMC 317 +#define HCLK_SDIO 318 +#define HCLK_EMMC 319 +#define HCLK_SDMMC_EXT 320 + +static struct rk_cru_gate rk3328_gates[] = { + /* CRU_CLKGATE_CON0 */ + CRU_GATE(0, "apll_core", "apll", 0x200, 0) + CRU_GATE(0, "dpll_core", "dpll", 0x200, 1) + CRU_GATE(0, "gpll_core", "gpll", 0x200, 2) + CRU_GATE(0, "npll_core", "npll", 0x200, 12) + + /* CRU_CLKGATE_CON4 */ + CRU_GATE(0, "gpll_peri", "gpll", 0x210, 0) + CRU_GATE(0, "cpll_peri", "cpll", 0x210, 1) + + /* CRU_CLKGATE_CON8 */ + CRU_GATE(0, "pclk_bus", "pclk_bus_pre", 0x220, 3) + CRU_GATE(0, "pclk_phy_pre", "pclk_bus_pre", 0x220, 4) + + /* CRU_CLKGATE_CON10 */ + CRU_GATE(ACLK_PERI, "aclk_peri", "aclk_peri_pre", 0x228, 0) + + /* CRU_CLKGATE_CON19 */ + CRU_GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0x24C, 0) + CRU_GATE(HCLK_SDIO, "hclk_sdio", "hclk_peri", 0x24C, 1) + CRU_GATE(HCLK_EMMC, "hclk_emmc", "hclk_peri", 0x24C, 2) + CRU_GATE(HCLK_SDMMC_EXT, "hclk_sdmmc_ext", "hclk_peri", 0x24C, 15) +}; + +/* + * PLLs + */ + +#define PLL_APLL 1 +#define PLL_DPLL 2 +#define PLL_CPLL 3 +#define PLL_GPLL 4 +#define PLL_NPLL 5 + +static const char *pll_parents[] = {"xin24m"}; +static struct rk_clk_pll_def apll = { + .clkdef = { + .id = PLL_APLL, + .name = "apll", + .parent_names = pll_parents, + .parent_cnt = nitems(pll_parents), + }, + .base_offset = 0x00, + .gate_offset = 0x200, + .gate_shift = 0, + .flags = RK_CLK_PLL_HAVE_GATE, +}; + +static struct rk_clk_pll_def dpll = { + .clkdef = { + .id = PLL_DPLL, + .name = "dpll", + .parent_names = pll_parents, + .parent_cnt = nitems(pll_parents), + }, + .base_offset = 0x20, + .gate_offset = 0x200, + .gate_shift = 1, + .flags = RK_CLK_PLL_HAVE_GATE, +}; + +static struct rk_clk_pll_def cpll = { + .clkdef = { + .id = PLL_CPLL, + .name = "cpll", + .parent_names = pll_parents, + .parent_cnt = nitems(pll_parents), + }, + .base_offset = 0x40, +}; + +static struct rk_clk_pll_def gpll = { + .clkdef = { + .id = PLL_GPLL, + .name = "gpll", + .parent_names = pll_parents, + .parent_cnt = nitems(pll_parents), + }, + .base_offset = 0x60, + .gate_offset = 0x200, + .gate_shift = 2, + .flags = RK_CLK_PLL_HAVE_GATE, +}; + +static struct rk_clk_pll_def npll = { + .clkdef = { + .id = PLL_NPLL, + .name = "npll", + .parent_names = pll_parents, + .parent_cnt = nitems(pll_parents), + }, + .base_offset = 0xa0, + .gate_offset = 0x200, + .gate_shift = 12, + .flags = RK_CLK_PLL_HAVE_GATE, +}; + +/* CRU_CLKSEL_CON0 */ +#define ACLK_BUS_PRE 136 + +/* Needs hdmiphy as parent too*/ +static const char *aclk_bus_pre_parents[] = {"cpll", "gpll"}; +static struct rk_clk_composite_def aclk_bus_pre = { + .clkdef = { + .id = ACLK_BUS_PRE, + .name = "aclk_bus_pre", + .parent_names = aclk_bus_pre_parents, + .parent_cnt = nitems(aclk_bus_pre_parents), + }, + .muxdiv_offset = 0x100, + .mux_shift = 13, + .mux_width = 2, + + .div_shift = 8, + .div_width = 5, + + .gate_offset = 0x232, + .gate_shift = 0, + + .flags = RK_CLK_COMPOSITE_HAVE_MUX | RK_CLK_COMPOSITE_HAVE_GATE, +}; + +/* CRU_CLKSEL_CON1 */ + +#define PCLK_BUS_PRE 216 +#define HCLK_BUS_PRE 328 + +static const char *hclk_bus_pre_parents[] = {"aclk_bus_pre"}; +static struct rk_clk_composite_def hclk_bus_pre = { + .clkdef = { + .id = HCLK_BUS_PRE, + .name = "hclk_bus_pre", + .parent_names = hclk_bus_pre_parents, + .parent_cnt = nitems(hclk_bus_pre_parents), + }, + .muxdiv_offset = 0x104, + + .div_shift = 8, + .div_width = 2, + + .gate_offset = 0x232, + .gate_shift = 1, + + .flags = RK_CLK_COMPOSITE_HAVE_GATE, +}; + +static const char *pclk_bus_pre_parents[] = {"aclk_bus_pre"}; +static struct rk_clk_composite_def pclk_bus_pre = { + .clkdef = { + .id = PCLK_BUS_PRE, + .name = "pclk_bus_pre", + .parent_names = pclk_bus_pre_parents, + .parent_cnt = nitems(pclk_bus_pre_parents), + }, + .muxdiv_offset = 0x104, + + .div_shift = 12, + .div_width = 3, + + .gate_offset = 0x232, + .gate_shift = 2, + + .flags = RK_CLK_COMPOSITE_HAVE_GATE, +}; + +/* CRU_CLKSEL_CON28 */ + +#define ACLK_PERI_PRE 137 + +static const char *aclk_peri_pre_parents[] = {"cpll", "gpll"/* , "hdmiphy" */}; +static struct rk_clk_composite_def aclk_peri_pre = { + .clkdef = { + .id = ACLK_PERI_PRE, + .name = "aclk_peri_pre", + .parent_names = aclk_peri_pre_parents, + .parent_cnt = nitems(aclk_peri_pre_parents), + }, + .muxdiv_offset = 0x170, + + .mux_shift = 6, + .mux_width = 2, + + .div_shift = 0, + .div_width = 5, + + .flags = RK_CLK_COMPOSITE_HAVE_MUX, +}; + +/* CRU_CLKSEL_CON29 */ + +#define PCLK_PERI 230 +#define HCLK_PERI 308 + +static const char *phclk_peri_parents[] = {"aclk_peri_pre"}; +static struct rk_clk_composite_def pclk_peri = { + .clkdef = { + .id = PCLK_PERI, + .name = "pclk_peri", + .parent_names = phclk_peri_parents, + .parent_cnt = nitems(phclk_peri_parents), + }, + + .div_shift = 0, + .div_width = 2, + + /* CRU_CLKGATE_CON10 */ + .gate_offset = 0x228, + .gate_shift = 2, + + .flags = RK_CLK_COMPOSITE_HAVE_GATE, +}; + +static struct rk_clk_composite_def hclk_peri = { + .clkdef = { + .id = HCLK_PERI, + .name = "hclk_peri", + .parent_names = phclk_peri_parents, + .parent_cnt = nitems(phclk_peri_parents), + }, + + .div_shift = 4, + .div_width = 3, + + /* CRU_CLKGATE_CON10 */ + .gate_offset = 0x228, + .gate_shift = 1, + + .flags = RK_CLK_COMPOSITE_HAVE_GATE, +}; + +/* CRU_CLKSEL_CON30 */ + +#define SCLK_SDMMC 33 + +static const char *mmc_parents[] = {"cpll", "gpll", "xin24m"/* , "usb480m" */}; +static struct rk_clk_composite_def sdmmc = { + .clkdef = { + .id = SCLK_SDMMC, + .name = "clk_sdmmc", + .parent_names = mmc_parents, + .parent_cnt = nitems(mmc_parents), + }, + .muxdiv_offset = 0x178, + + .mux_shift = 8, + .mux_width = 2, + + .div_shift = 0, + .div_width = 8, + + /* CRU_CLKGATE_CON4 */ + .gate_offset = 0x210, + .gate_shift = 3, + + .flags = RK_CLK_COMPOSITE_HAVE_MUX | RK_CLK_COMPOSITE_HAVE_GATE, +}; + +/* CRU_CLKSEL_CON31 */ +#define SCLK_SDIO 34 + +static struct rk_clk_composite_def sdio = { + .clkdef = { + .id = SCLK_SDIO, + .name = "clk_sdio", + .parent_names = mmc_parents, + .parent_cnt = nitems(mmc_parents), + }, + .muxdiv_offset = 0x17C, + + .mux_shift = 8, + .mux_width = 2, + + .div_shift = 0, + .div_width = 8, + + /* CRU_CLKGATE_CON4 */ + .gate_offset = 0x210, + .gate_shift = 4, + + .flags = RK_CLK_COMPOSITE_HAVE_MUX | RK_CLK_COMPOSITE_HAVE_GATE, +}; + +/* CRU_CLKSEL_CON32 */ +#define SCLK_EMMC 35 + +static struct rk_clk_composite_def emmc = { + .clkdef = { + .id = SCLK_EMMC, + .name = "clk_emmc", + .parent_names = mmc_parents, + .parent_cnt = nitems(mmc_parents), + }, + .muxdiv_offset = 0x180, + + .mux_shift = 8, + .mux_width = 2, + + .div_shift = 0, + .div_width = 8, + + /* CRU_CLKGATE_CON4 */ + .gate_offset = 0x210, + .gate_shift = 5, + + .flags = RK_CLK_COMPOSITE_HAVE_MUX | RK_CLK_COMPOSITE_HAVE_GATE, +}; + +static struct rk_clk rk3328_clks[] = { + [PLL_APLL] = { + .type = RK_CLK_PLL, + .clk.pll = &apll + }, + [PLL_DPLL] = { + .type = RK_CLK_PLL, + .clk.pll = &dpll + }, + [PLL_CPLL] = { + .type = RK_CLK_PLL, + .clk.pll = &cpll + }, + [PLL_GPLL] = { + .type = RK_CLK_PLL, + .clk.pll = &gpll + }, + [PLL_NPLL] = { + .type = RK_CLK_PLL, + .clk.pll = &npll + }, + + [ACLK_BUS_PRE] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &aclk_bus_pre + }, + [HCLK_BUS_PRE] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &hclk_bus_pre + }, + [PCLK_BUS_PRE] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &pclk_bus_pre + }, + + [ACLK_PERI_PRE] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &aclk_peri_pre, + }, + [PCLK_PERI] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &pclk_peri, + }, + [HCLK_PERI] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &hclk_peri, + }, + [SCLK_SDMMC] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &sdmmc + }, + [SCLK_SDIO] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &sdio + }, + [SCLK_EMMC] = { + .type = RK_CLK_COMPOSITE, + .clk.composite = &emmc + }, +}; + +static int +rk3328_cru_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "rockchip,rk3328-cru")) { + device_set_desc(dev, "Rockchip RK3328 Clock and Reset Unit"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +rk3328_cru_attach(device_t dev) +{ + struct rk_cru_softc *sc; + + sc = device_get_softc(dev); + sc->dev = dev; + + sc->gates = rk3328_gates; + sc->ngates = nitems(rk3328_gates); + + sc->clks = rk3328_clks; + sc->nclks = nitems(rk3328_clks); + + return (rk_cru_attach(dev)); +} + +static device_method_t rk3328_cru_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, rk3328_cru_probe), + DEVMETHOD(device_attach, rk3328_cru_attach), + + DEVMETHOD_END +}; + +static devclass_t rk3328_cru_devclass; + +DEFINE_CLASS_1(rk3328_cru, rk3328_cru_driver, rk3328_cru_methods, + sizeof(struct rk_cru_softc), rk_cru_driver); + +EARLY_DRIVER_MODULE(rk3328_cru, simplebus, rk3328_cru_driver, + rk3328_cru_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm64/rockchip/clk/rk_clk_composite.c b/sys/arm64/rockchip/clk/rk_clk_composite.c new file mode 100644 index 000000000000..80277b48f30a --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_composite.c @@ -0,0 +1,255 @@ +/*- + * 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_composite_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; +}; + +#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_COMPOSITE_WRITE_MASK 0xFFFF0000 + +static int +rk_clk_composite_init(struct clknode *clk, device_t dev) +{ + struct rk_clk_composite_sc *sc; + uint32_t val, idx; + + sc = clknode_get_softc(clk); + + idx = 0; + if ((sc->flags & RK_CLK_COMPOSITE_HAVE_MUX) != 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_composite_set_gate(struct clknode *clk, bool enable) +{ + struct rk_clk_composite_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if ((sc->flags & RK_CLK_COMPOSITE_HAVE_GATE) == 0) + return (0); + + DEVICE_LOCK(clk); + READ4(clk, sc->gate_offset, &val); + if (enable) + val &= ~(1 << sc->gate_shift); + else + val |= 1 << sc->gate_shift; + WRITE4(clk, sc->gate_offset, val | RK_CLK_COMPOSITE_MASK); + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +rk_clk_composite_set_mux(struct clknode *clk, int index) +{ + struct rk_clk_composite_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if ((sc->flags & RK_CLK_COMPOSITE_HAVE_MUX) == 0) + return (0); + + 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_composite_recalc(struct clknode *clk, uint64_t *freq) +{ + struct rk_clk_composite_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_composite_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct rk_clk_composite_sc *sc; + uint64_t best, cur; + uint32_t div, best_div, val; + + sc = clknode_get_softc(clk); + + for (best = 0, best_div = 0, div = 0; div <= sc->div_mask; div++) { + cur = fparent / div; + if ((*fout - cur) < (*fout - best)) { + best = cur; + best_div = div; + break; + } + } + + if (best_div == 0) + return (0); + + if ((best < *fout) && + ((flags & CLK_SET_ROUND_DOWN) == 0)) { + *stop = 1; + return (ERANGE); + } + if ((best > *fout) && + ((flags & CLK_SET_ROUND_UP) == 0)) { + *stop = 1; + return (ERANGE); + } + + 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 |= (best_div - 1) << sc->div_shift; + WRITE4(clk, sc->muxdiv_offset, val | RK_CLK_COMPOSITE_MASK); + DEVICE_UNLOCK(clk); + + *fout = best; + *stop = 1; + + return (0); +} + +static clknode_method_t rk_clk_composite_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, rk_clk_composite_init), + CLKNODEMETHOD(clknode_set_gate, rk_clk_composite_set_gate), + CLKNODEMETHOD(clknode_set_mux, rk_clk_composite_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, rk_clk_composite_recalc), + CLKNODEMETHOD(clknode_set_freq, rk_clk_composite_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(rk_clk_composite_clknode, rk_clk_composite_clknode_class, + rk_clk_composite_clknode_methods, sizeof(struct rk_clk_composite_sc), + clknode_class); + +int +rk_clk_composite_register(struct clkdom *clkdom, struct rk_clk_composite_def *clkdef) +{ + struct clknode *clk; + struct rk_clk_composite_sc *sc; + + clk = clknode_create(clkdom, &rk_clk_composite_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->gate_offset = clkdef->gate_offset; + sc->gate_shift = clkdef->gate_shift; + + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + + return (0); +} diff --git a/sys/arm64/rockchip/clk/rk_clk_composite.h b/sys/arm64/rockchip/clk/rk_clk_composite.h new file mode 100644 index 000000000000..12a14fd95205 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_composite.h @@ -0,0 +1,61 @@ +/*- + * 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_COMPOSITE_H_ +#define _RK_CLK_COMPOSITE_H_ + +#include + +struct rk_clk_composite_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 gate_offset; + uint32_t gate_shift; + + uint32_t flags; +}; + +#define RK_CLK_COMPOSITE_HAVE_MUX 0x0001 +#define RK_CLK_COMPOSITE_HAVE_GATE 0x0002 + +#define RK_CLK_COMPOSITE_MASK 0xFFFF0000 + +int rk_clk_composite_register(struct clkdom *clkdom, + struct rk_clk_composite_def *clkdef); + +#endif /* _RK_CLK_COMPOSITE_H_ */ diff --git a/sys/arm64/rockchip/clk/rk_clk_gate.c b/sys/arm64/rockchip/clk/rk_clk_gate.c new file mode 100644 index 000000000000..0a43cea087bd --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_gate.c @@ -0,0 +1,135 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2016 Michal Meloun + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include + +#include "clkdev_if.h" + +#define WR4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define RD4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define MD4(_clk, off, clr, set ) \ + CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +static int rk_clk_gate_init(struct clknode *clk, device_t dev); +static int rk_clk_gate_set_gate(struct clknode *clk, bool enable); +struct rk_clk_gate_sc { + uint32_t offset; + uint32_t shift; + uint32_t mask; + uint32_t on_value; + uint32_t off_value; + int gate_flags; + bool ungated; +}; + +static clknode_method_t rk_clk_gate_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, rk_clk_gate_init), + CLKNODEMETHOD(clknode_set_gate, rk_clk_gate_set_gate), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(rk_clk_gate, rk_clk_gate_class, rk_clk_gate_methods, + sizeof(struct rk_clk_gate_sc), clknode_class); + +static int +rk_clk_gate_init(struct clknode *clk, device_t dev) +{ + uint32_t reg; + struct rk_clk_gate_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); + rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + if (rv != 0) + return (rv); + reg = (reg >> sc->shift) & sc->mask; + sc->ungated = reg == sc->on_value ? 1 : 0; + clknode_init_parent_idx(clk, 0); + return(0); +} + +static int +rk_clk_gate_set_gate(struct clknode *clk, bool enable) +{ + uint32_t reg; + struct rk_clk_gate_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + sc->ungated = enable; + DEVICE_LOCK(clk); + rv = MD4(clk, sc->offset, sc->mask << sc->shift, + ((sc->ungated ? sc->on_value : sc->off_value) << sc->shift) | + RK_CLK_GATE_MASK); + if (rv != 0) { + DEVICE_UNLOCK(clk); + return (rv); + } + RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + return(0); +} + +int +rk_clk_gate_register(struct clkdom *clkdom, struct rk_clk_gate_def *clkdef) +{ + struct clknode *clk; + struct rk_clk_gate_sc *sc; + + clk = clknode_create(clkdom, &rk_clk_gate_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->offset = clkdef->offset; + sc->shift = clkdef->shift; + sc->mask = clkdef->mask; + sc->on_value = clkdef->on_value; + sc->off_value = clkdef->off_value; + sc->gate_flags = clkdef->gate_flags; + + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/arm64/rockchip/clk/rk_clk_gate.h b/sys/arm64/rockchip/clk/rk_clk_gate.h new file mode 100644 index 000000000000..20b63e49c66d --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_gate.h @@ -0,0 +1,50 @@ +/*- + * 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_GATE_H_ +#define _RK_CLK_GATE_H_ + +#include + +struct rk_clk_gate_def { + struct clknode_init_def clkdef; + uint32_t offset; + uint32_t shift; + uint32_t mask; + uint32_t on_value; + uint32_t off_value; + int gate_flags; +}; + +#define RK_CLK_GATE_MASK 0xFFFF0000 + +int rk_clk_gate_register(struct clkdom *clkdom, struct rk_clk_gate_def *clkdef); + +#endif /* _RK_CLK_GATE_H_ */ diff --git a/sys/arm64/rockchip/clk/rk_clk_mux.c b/sys/arm64/rockchip/clk/rk_clk_mux.c new file mode 100644 index 000000000000..f8ae8b503361 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_mux.c @@ -0,0 +1,139 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2016 Michal Meloun + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include "clkdev_if.h" + +#define WR4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define RD4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define MD4(_clk, off, clr, set ) \ + CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +static int rk_clk_mux_init(struct clknode *clk, device_t dev); +static int rk_clk_mux_set_mux(struct clknode *clk, int idx); + +struct rk_clk_mux_sc { + uint32_t offset; + uint32_t shift; + uint32_t mask; + int mux_flags; +}; + +static clknode_method_t rk_clk_mux_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, rk_clk_mux_init), + CLKNODEMETHOD(clknode_set_mux, rk_clk_mux_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(rk_clk_mux, rk_clk_mux_class, rk_clk_mux_methods, + sizeof(struct rk_clk_mux_sc), clknode_class); + + +static int +rk_clk_mux_init(struct clknode *clk, device_t dev) +{ + uint32_t reg; + struct rk_clk_mux_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + if (rv != 0) { + return (rv); + } + reg = (reg >> sc->shift) & sc->mask; + clknode_init_parent_idx(clk, reg); + return(0); +} + +static int +rk_clk_mux_set_mux(struct clknode *clk, int idx) +{ + uint32_t reg; + struct rk_clk_mux_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + rv = MD4(clk, sc->offset, sc->mask << sc->shift, + ((idx & sc->mask) << sc->shift) | RK_CLK_MUX_MASK); + if (rv != 0) { + DEVICE_UNLOCK(clk); + return (rv); + } + RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + + return(0); +} + +int +rk_clk_mux_register(struct clkdom *clkdom, struct rk_clk_mux_def *clkdef) +{ + struct clknode *clk; + struct rk_clk_mux_sc *sc; + + clk = clknode_create(clkdom, &rk_clk_mux_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->offset = clkdef->offset; + sc->shift = clkdef->shift; + sc->mask = (1 << clkdef->width) - 1; + sc->mux_flags = clkdef->mux_flags; + + clknode_register(clkdom, clk); + return (0); +} diff --git a/sys/arm64/rockchip/clk/rk_clk_mux.h b/sys/arm64/rockchip/clk/rk_clk_mux.h new file mode 100644 index 000000000000..f44443790b18 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_mux.h @@ -0,0 +1,47 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2016 Michal Meloun + * 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_MUX_H_ +#define _RK_CLK_MUX_H_ + +#include + +struct rk_clk_mux_def { + struct clknode_init_def clkdef; + uint32_t offset; + uint32_t shift; + uint32_t width; + int mux_flags; +}; + +#define RK_CLK_MUX_MASK 0xFFFF0000 + +int rk_clk_mux_register(struct clkdom *clkdom, struct rk_clk_mux_def *clkdef); + +#endif /* _RK_CLK_MUX_H_ */ diff --git a/sys/arm64/rockchip/clk/rk_clk_pll.c b/sys/arm64/rockchip/clk/rk_clk_pll.c new file mode 100644 index 000000000000..6539f5424ef0 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_pll.c @@ -0,0 +1,203 @@ +/*- + * 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_pll_sc { + uint32_t base_offset; + + uint32_t gate_offset; + uint32_t gate_shift; + + uint32_t flags; +}; + +#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_CLK_PLL_DSMPD_OFFSET 4 +#define RK_CLK_PLL_DSMPD_SHIFT 12 +#define RK_CLK_PLL_DSMPD_MASK 0x1000 + +#define RK_CLK_PLL_REFDIV_OFFSET 4 +#define RK_CLK_PLL_REFDIV_SHIFT 0 +#define RK_CLK_PLL_REFDIV_MASK 0x3F + +#define RK_CLK_PLL_FBDIV_OFFSET 0 +#define RK_CLK_PLL_FBDIV_SHIFT 0 +#define RK_CLK_PLL_FBDIV_MASK 0xFFF + +#define RK_CLK_PLL_POSTDIV1_OFFSET 0 +#define RK_CLK_PLL_POSTDIV1_SHIFT 12 +#define RK_CLK_PLL_POSTDIV1_MASK 0x7000 + +#define RK_CLK_PLL_POSTDIV2_OFFSET 4 +#define RK_CLK_PLL_POSTDIV2_SHIFT 6 +#define RK_CLK_PLL_POSTDIV2_MASK 0x1C0 + +#define RK_CLK_PLL_FRAC_OFFSET 8 +#define RK_CLK_PLL_FRAC_SHIFT 0 +#define RK_CLK_PLL_FRAC_MASK 0xFFFFFF + +static int +rk_clk_pll_init(struct clknode *clk, device_t dev) +{ + struct rk_clk_pll_sc *sc; + + sc = clknode_get_softc(clk); + + clknode_init_parent_idx(clk, 0); + + return (0); +} + +static int +rk_clk_pll_set_gate(struct clknode *clk, bool enable) +{ + struct rk_clk_pll_sc *sc; + uint32_t val; + + sc = clknode_get_softc(clk); + + if ((sc->flags & RK_CLK_PLL_HAVE_GATE) == 0) + return (0); + + DEVICE_LOCK(clk); + READ4(clk, sc->gate_offset, &val); + if (enable) + val &= ~(1 << sc->gate_shift); + else + val |= 1 << sc->gate_shift; + WRITE4(clk, sc->gate_offset, val | RK_CLK_PLL_MASK); + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +rk_clk_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct rk_clk_pll_sc *sc; + uint64_t foutvco; + uint32_t dsmpd, refdiv, fbdiv; + uint32_t postdiv1, postdiv2, frac; + uint32_t raw1, raw2; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + + READ4(clk, sc->base_offset, &raw1); + READ4(clk, sc->base_offset + 4, &raw2); + + READ4(clk, sc->base_offset + RK_CLK_PLL_DSMPD_OFFSET, &dsmpd); + dsmpd = (dsmpd & RK_CLK_PLL_DSMPD_MASK) >> RK_CLK_PLL_DSMPD_SHIFT; + + READ4(clk, sc->base_offset + RK_CLK_PLL_REFDIV_OFFSET, &refdiv); + refdiv = (refdiv & RK_CLK_PLL_REFDIV_MASK) >> RK_CLK_PLL_REFDIV_SHIFT; + + READ4(clk, sc->base_offset + RK_CLK_PLL_FBDIV_OFFSET, &fbdiv); + fbdiv = (fbdiv & RK_CLK_PLL_FBDIV_MASK) >> RK_CLK_PLL_FBDIV_SHIFT; + + READ4(clk, sc->base_offset + RK_CLK_PLL_POSTDIV1_OFFSET, &postdiv1); + postdiv1 = (postdiv1 & RK_CLK_PLL_POSTDIV1_MASK) >> RK_CLK_PLL_POSTDIV1_SHIFT; + + READ4(clk, sc->base_offset + RK_CLK_PLL_POSTDIV2_OFFSET, &postdiv2); + postdiv2 = (postdiv2 & RK_CLK_PLL_POSTDIV2_MASK) >> RK_CLK_PLL_POSTDIV2_SHIFT; + + READ4(clk, sc->base_offset + RK_CLK_PLL_FRAC_OFFSET, &frac); + frac = (frac & RK_CLK_PLL_FRAC_MASK) >> RK_CLK_PLL_FRAC_SHIFT; + + DEVICE_UNLOCK(clk); + + if (dsmpd == 0) { + /* Fractional mode */ + foutvco = *freq / refdiv * (fbdiv + frac / 224); + } else { + /* Integer mode */ + + foutvco = *freq / refdiv * fbdiv; + } + + *freq = foutvco / postdiv1 / postdiv2; + + return (0); +} + +static clknode_method_t rk_clk_pll_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, rk_clk_pll_init), + CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, rk_clk_pll_recalc), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(rk_clk_pll_clknode, rk_clk_pll_clknode_class, + rk_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); + +int +rk_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) +{ + struct clknode *clk; + struct rk_clk_pll_sc *sc; + + clk = clknode_create(clkdom, &rk_clk_pll_clknode_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + + sc->base_offset = clkdef->base_offset; + sc->gate_offset = clkdef->gate_offset; + sc->gate_shift = clkdef->gate_shift; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + + return (0); +} diff --git a/sys/arm64/rockchip/clk/rk_clk_pll.h b/sys/arm64/rockchip/clk/rk_clk_pll.h new file mode 100644 index 000000000000..ee6364dc8539 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_clk_pll.h @@ -0,0 +1,52 @@ +/*- + * 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_PLL_H_ +#define _RK_CLK_PLL_H_ + +#include + +struct rk_clk_pll_def { + struct clknode_init_def clkdef; + uint32_t base_offset; + + uint32_t gate_offset; + uint32_t gate_shift; + + uint32_t flags; +}; + +#define RK_CLK_PLL_HAVE_GATE 0x1 + +#define RK_CLK_PLL_MASK 0xFFFF0000 + +int rk_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef); + +#endif /* _RK_CLK_PLL_H_ */ diff --git a/sys/arm64/rockchip/clk/rk_cru.c b/sys/arm64/rockchip/clk/rk_cru.c new file mode 100644 index 000000000000..d3f57916e1da --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_cru.c @@ -0,0 +1,267 @@ +/*- + * 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$ + */ + +/* + * RockChip Clock and Reset Unit + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "clkdev_if.h" +#include "hwreset_if.h" + +static struct resource_spec rk_cru_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define CCU_READ4(sc, reg) bus_read_4((sc)->res, (reg)) +#define CCU_WRITE4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) + +void rk3328_cru_register_clocks(struct rk_cru_softc *sc); + +static int +rk_cru_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct rk_cru_softc *sc; + + sc = device_get_softc(dev); + CCU_WRITE4(sc, addr, val); + return (0); +} + +static int +rk_cru_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct rk_cru_softc *sc; + + sc = device_get_softc(dev); + + *val = CCU_READ4(sc, addr); + return (0); +} + +static int +rk_cru_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) +{ + struct rk_cru_softc *sc; + uint32_t reg; + + sc = device_get_softc(dev); + + reg = CCU_READ4(sc, addr); + reg &= ~clr; + reg |= set; + CCU_WRITE4(sc, addr, reg); + + return (0); +} + +static int +rk_cru_reset_assert(device_t dev, intptr_t id, bool reset) +{ + struct rk_cru_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + if (id >= sc->nresets || sc->resets[id].offset == 0) + return (0); + + mtx_lock(&sc->mtx); + val = CCU_READ4(sc, sc->resets[id].offset); + if (reset) + val &= ~(1 << sc->resets[id].shift); + else + val |= 1 << sc->resets[id].shift; + CCU_WRITE4(sc, sc->resets[id].offset, val); + mtx_unlock(&sc->mtx); + + return (0); +} + +static int +rk_cru_reset_is_asserted(device_t dev, intptr_t id, bool *reset) +{ + struct rk_cru_softc *sc; + uint32_t val; + + sc = device_get_softc(dev); + + if (id >= sc->nresets || sc->resets[id].offset == 0) + return (0); + + mtx_lock(&sc->mtx); + val = CCU_READ4(sc, sc->resets[id].offset); + *reset = (val & (1 << sc->resets[id].shift)) != 0 ? false : true; + mtx_unlock(&sc->mtx); + + return (0); +} + +static void +rk_cru_device_lock(device_t dev) +{ + struct rk_cru_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} + +static void +rk_cru_device_unlock(device_t dev) +{ + struct rk_cru_softc *sc; + + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); +} + +static int +rk_cru_register_gates(struct rk_cru_softc *sc) +{ + struct rk_clk_gate_def def; + int i; + + for (i = 0; i < sc->ngates; i++) { + if (sc->gates[i].name == NULL) + continue; + memset(&def, 0, sizeof(def)); + def.clkdef.id = sc->gates[i].id; + def.clkdef.name = sc->gates[i].name; + def.clkdef.parent_names = &sc->gates[i].parent_name; + def.clkdef.parent_cnt = 1; + def.offset = sc->gates[i].offset; + def.shift = sc->gates[i].shift; + def.mask = 1; + def.on_value = 0; + def.off_value = 1; + rk_clk_gate_register(sc->clkdom, &def); + } + + return (0); +} + +int +rk_cru_attach(device_t dev) +{ + struct rk_cru_softc *sc; + int i; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, rk_cru_spec, &sc->res) != 0) { + device_printf(dev, "cannot allocate resources for device\n"); + return (ENXIO); + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + sc->clkdom = clkdom_create(dev); + if (sc->clkdom == NULL) + panic("Cannot create clkdom\n"); + + for (i = 0; i < sc->nclks; i++) { + switch (sc->clks[i].type) { + case RK_CLK_UNDEFINED: + break; + case RK_CLK_PLL: + rk_clk_pll_register(sc->clkdom, sc->clks[i].clk.pll); + break; + case RK_CLK_COMPOSITE: + rk_clk_composite_register(sc->clkdom, + sc->clks[i].clk.composite); + break; + case RK_CLK_MUX: + rk_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux); + default: + device_printf(dev, "Unknown clock type\n"); + return (ENXIO); + break; + } + } + if (sc->gates) + rk_cru_register_gates(sc); + + if (clkdom_finit(sc->clkdom) != 0) + panic("cannot finalize clkdom initialization\n"); + + if (bootverbose) + clkdom_dump(sc->clkdom); + + /* If we have resets, register our self as a reset provider */ + if (sc->resets) + hwreset_register_ofw_provider(dev); + + return (0); +} + +static device_method_t rk_cru_methods[] = { + /* clkdev interface */ + DEVMETHOD(clkdev_write_4, rk_cru_write_4), + DEVMETHOD(clkdev_read_4, rk_cru_read_4), + DEVMETHOD(clkdev_modify_4, rk_cru_modify_4), + DEVMETHOD(clkdev_device_lock, rk_cru_device_lock), + DEVMETHOD(clkdev_device_unlock, rk_cru_device_unlock), + + /* Reset interface */ + DEVMETHOD(hwreset_assert, rk_cru_reset_assert), + DEVMETHOD(hwreset_is_asserted, rk_cru_reset_is_asserted), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(rk_cru, rk_cru_driver, rk_cru_methods, + sizeof(struct rk_cru_softc)); diff --git a/sys/arm64/rockchip/clk/rk_cru.h b/sys/arm64/rockchip/clk/rk_cru.h new file mode 100644 index 000000000000..9541cda63d76 --- /dev/null +++ b/sys/arm64/rockchip/clk/rk_cru.h @@ -0,0 +1,95 @@ +/*- + * 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$ + */ + +#ifndef __RK_CRU_H__ +#define __RK_CRU_H__ + +#include +#include +#include +#include + +struct rk_cru_reset { + uint32_t offset; + uint32_t shift; +}; + +struct rk_cru_gate { + const char *name; + const char *parent_name; + uint32_t id; + uint32_t offset; + uint32_t shift; +}; + +#define CRU_GATE(idx, clkname, pname, o, s) \ + { \ + .id = idx, \ + .name = clkname, \ + .parent_name = pname, \ + .offset = o, \ + .shift = s, \ + }, + +enum rk_clk_type { + RK_CLK_UNDEFINED = 0, + RK_CLK_PLL, + RK_CLK_COMPOSITE, + RK_CLK_MUX, +}; + +struct rk_clk { + enum rk_clk_type type; + union { + struct rk_clk_pll_def *pll; + struct rk_clk_composite_def *composite; + struct rk_clk_mux_def *mux; + } clk; +}; + +struct rk_cru_softc { + device_t dev; + struct resource *res; + struct clkdom *clkdom; + struct mtx mtx; + int type; + struct rk_cru_reset *resets; + int nresets; + struct rk_cru_gate *gates; + int ngates; + struct rk_clk *clks; + int nclks; +}; + +DECLARE_CLASS(rk_cru_driver); + +int rk_cru_attach(device_t dev); + +#endif /* __RK_CRU_H__ */ diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index b823291af6da..020dab2262cf 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -228,3 +228,10 @@ cddl/contrib/opensolaris/common/atomic/aarch64/opensolaris_atomic.S optional zfs cddl/dev/dtrace/aarch64/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}" cddl/dev/dtrace/aarch64/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}" cddl/dev/fbt/aarch64/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}" + +arm64/rockchip/clk/rk_cru.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 +arm64/rockchip/clk/rk_clk_pll.c optional fdt soc_rockchip_rk3328 +arm64/rockchip/clk/rk3328_cru.c optional fdt soc_rockchip_rk3328 diff --git a/sys/conf/options.arm64 b/sys/conf/options.arm64 index 0651acc6cdc0..0e8fdf7a1540 100644 --- a/sys/conf/options.arm64 +++ b/sys/conf/options.arm64 @@ -22,3 +22,4 @@ SOC_ALLWINNER_H5 opt_soc.h SOC_BRCM_BCM2837 opt_soc.h SOC_CAVM_THUNDERX opt_soc.h SOC_HISI_HI6220 opt_soc.h +SOC_ROCKCHIP_RK3328 opt_soc.h