Index: sys/arm64/conf/GENERIC =================================================================== --- sys/arm64/conf/GENERIC +++ sys/arm64/conf/GENERIC @@ -332,6 +332,7 @@ device regulator device syscon device aw_syscon +device qoriq_clk # IO Domains device rk_iodomain Index: sys/arm64/qoriq/clk/qoriq_clkgen.h =================================================================== --- /dev/null +++ sys/arm64/qoriq/clk/qoriq_clkgen.h @@ -0,0 +1,61 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Alstom Group. + * Copyright (c) 2020 Semihalf. + * + * 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. + */ + +#ifndef _QORIQ_CLKGEN_H_ +#define _QORIQ_CLKGEN_H_ + +#define QORIQ_LITTLE_ENDIAN 0x01 + +struct qoriq_clk_info { + uint32_t flags; + struct clk_mux_def *hwaccel_defs; + uint8_t num_hwaccel; + const char **fman_src; + uint8_t num_fman; + struct clk_mux_def *cmux_defs; + uint8_t num_cmux; + uint8_t num_cgapll; +}; + +struct qoriq_clkgen_softc { + device_t dev; + struct clkdom *clkdom; + uint64_t sysclk_freq; + uint64_t coreclk_freq; + + struct qoriq_clk_info *info; + + struct resource *res; + struct mtx mtx; +}; + +DECLARE_CLASS(qoriq_clkgen_driver); + +int qoriq_clkgen_attach(device_t dev); + +#endif /* _QORIQ_CLKGEN_H_ */ Index: sys/arm64/qoriq/clk/qoriq_clkgen.c =================================================================== --- /dev/null +++ sys/arm64/qoriq/clk/qoriq_clkgen.c @@ -0,0 +1,481 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Alstom Group. + * Copyright (c) 2020 Semihalf. + * + * 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. + */ + +/* + * Driver for clock generators for QorIQ family of processors. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include + +#include "clkdev_if.h" + +#define PLTFRM_PLL_OFFSET 0xC00 +#define PLTFRM_PLL_MASK 0x3E +#define CGA_PLL_BASE_OFFSET 0x800 +#define CGB_PLL_BASE_OFFSET 0x860 +#define CGA_PLL_MASK 0x1FE +#define CORE_PLL_DIV_MAX 4 +#define PLL_KILL_BIT (1 << 31) + +static int qoriq_clkgen_ofw_mapper(struct clkdom*, uint32_t, phandle_t*, + struct clknode**); +static int qoriq_clkgen_write_4(device_t, bus_addr_t, uint32_t); +static int qoriq_clkgen_read_4(device_t, bus_addr_t, uint32_t*); +static int qoriq_clkgen_modify_4(device_t, bus_addr_t, uint32_t, uint32_t); +static void qoriq_clkgen_lock(device_t); +static void qoriq_clkgen_unlock(device_t); +static int qoriq_clkgen_create_sysclk(device_t); +static int qoriq_clkgen_create_coreclk(device_t); +static int qoriq_clkgen_create_plls(device_t); +static int qoriq_clkgen_create_hwaccel(device_t); +static int qoriq_clkgen_create_fman(device_t); + +static device_method_t qoriq_clkgen_methods[] = { + DEVMETHOD(clkdev_write_4, qoriq_clkgen_write_4), + DEVMETHOD(clkdev_read_4, qoriq_clkgen_read_4), + DEVMETHOD(clkdev_modify_4, qoriq_clkgen_modify_4), + DEVMETHOD(clkdev_device_lock, qoriq_clkgen_lock), + DEVMETHOD(clkdev_device_unlock, qoriq_clkgen_unlock), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(qoriq_clkgen, qoriq_clkgen_driver, qoriq_clkgen_methods, + sizeof(struct qoriq_clkgen_softc)); + +static struct resource_spec qoriq_clkgen_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +/* Strings for clock node name generation. */ +static const char *clk_names[] = { + "sysclk", + "cmux", + "hwaccel", + "fman", + "pltfrm", + "coreclk" +}; + +static int +qoriq_clkgen_ofw_mapper(struct clkdom *clkdom, uint32_t ncells, + phandle_t *cells, struct clknode **clk) +{ + char namebuf[24]; + + if (ncells != 2) + return (EINVAL); + + if (cells[0] > 5) + return (EINVAL); + + snprintf(namebuf, 24, "cg-%s-%d", clk_names[cells[0]], cells[1]); + *clk = clknode_find_by_name(namebuf); + + if (clk == NULL) + return (EINVAL); + + return (0); +} + +static int +qoriq_clkgen_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct qoriq_clkgen_softc *sc; + + sc = device_get_softc(dev); + + if (sc->info->flags & QORIQ_LITTLE_ENDIAN) + bus_write_4(sc->res, addr, val); + else + bus_write_4(sc->res, addr, htobe32(val)); + + return (0); +} + +static int +qoriq_clkgen_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct qoriq_clkgen_softc *sc; + + sc = device_get_softc(dev); + + if (sc->info->flags & QORIQ_LITTLE_ENDIAN) + *val = bus_read_4(sc->res, addr); + else + *val = be32toh(bus_read_4(sc->res, addr)); + + return (0); +} + +static int +qoriq_clkgen_modify_4(device_t dev, bus_addr_t addr, + uint32_t clr, uint32_t set) +{ + uint32_t tmp; + + qoriq_clkgen_read_4(dev, addr, &tmp); + tmp &= ~(clr); + tmp |= set; + qoriq_clkgen_write_4(dev, addr, tmp); + + return (0); +} + +static void +qoriq_clkgen_lock(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock(&sc->mtx); +} + +static void +qoriq_clkgen_unlock(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + + sc = device_get_softc(dev); + + mtx_unlock(&sc->mtx); +} + +static int +qoriq_clkgen_create_sysclk(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + struct clk_fixed_def def; + phandle_t node; + clk_t sysclk; + int error; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + sc->sysclk_freq = 0; + + memset(&def, 0, sizeof(struct clk_fixed_def)); + def.clkdef.name = "cg-sysclk-0"; + def.clkdef.id = 0; + + error = clk_get_by_ofw_name(dev, node, "sysclk", &sysclk); + if (error != 0) { + error = clk_get_by_ofw_index(dev, node, 0, &sysclk); + if (error != 0) + return (error); + } + + error = clk_get_freq(sysclk, &def.freq); + if (error != 0) + return (error); + + sc->sysclk_freq = def.freq; + + error = clknode_fixed_register(sc->clkdom, &def); + + return (error); +} + + +static int +qoriq_clkgen_create_coreclk(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + struct clk_fixed_def def; + phandle_t node; + clk_t coreclk; + int error; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + sc->coreclk_freq = 0; + + memset(&def, 0, sizeof(struct clk_fixed_def)); + def.clkdef.name = "cg-coreclk-0"; + def.clkdef.id = 500; + + error = clk_get_by_ofw_name(dev, node, "coreclk", &coreclk); + if (error != 0) + return (error); + + error = clk_get_freq(coreclk, &def.freq); + if (error != 0) + return (error); + + sc->coreclk_freq = def.freq; + + error = clknode_fixed_register(sc->clkdom, &def); + + return (error); +} + +/* + * During creation of PLL nodes the driver reads from PLLs once + * and then it creates those nodes as fixed clock nodes. No writing + * to PLL nodes is implemented. + */ +static int +qoriq_clkgen_create_plls(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + struct clk_fixed_def def; + uint64_t clksrc_freq; + bus_addr_t address; + uint32_t pll_val; + char namebuf[24]; + int i, j, error; + + sc = device_get_softc(dev); + + memset(&def, 0, sizeof(struct clk_fixed_def)); + def.clkdef.name = namebuf; + + CLKDEV_READ_4(dev, PLTFRM_PLL_OFFSET, &pll_val); + pll_val = (pll_val & PLTFRM_PLL_MASK) >> 1; + + for (i = 1; i <= 16; i *= 2) { + snprintf(namebuf, 24, "cg-pltfrm-%d", i - 1); + def.clkdef.id = 400 + i - 1; + def.freq = (sc->sysclk_freq * pll_val / i); + + error = clknode_fixed_register(sc->clkdom, &def); + if (error != 0) { + return (error); + } + } + + if (sc->coreclk_freq == 0) + clksrc_freq = sc->sysclk_freq; + else + clksrc_freq = sc->coreclk_freq; + + for (i = 1; i <= sc->info->num_cgapll; i++) { + address = CGA_PLL_BASE_OFFSET + (i - 1) * 0x20; + CLKDEV_READ_4(dev, address, &pll_val); + if (pll_val & PLL_KILL_BIT) + pll_val = 1; + else + pll_val = (pll_val & CGA_PLL_MASK) >> 1; + + + for (j = 0; j < CORE_PLL_DIV_MAX; j++) { + snprintf(namebuf, 24, "cg-cgapll%d-%d", i, j); + def.freq = (clksrc_freq * pll_val) / (j+1); + + error = clknode_fixed_register(sc->clkdom, &def); + if (error != 0) + return (error); + } + } + + return (0); +} + +static int +qoriq_clkgen_create_hwaccel(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + char namebuf[24]; + int i, error; + + sc = device_get_softc(dev); + + for (i = 0; i < sc->info->num_hwaccel; i++) { + sc->info->hwaccel_defs[i].clkdef.id = 200 + i; + sc->info->hwaccel_defs[i].clkdef.name = namebuf; + + snprintf(namebuf, 24, "cg-hwaccel-%d", i); + + error = clknode_mux_register(sc->clkdom, + &(sc->info->hwaccel_defs[i])); + sc->info->hwaccel_defs[i].clkdef.name = NULL; + if (error != 0) + return (error); + } + + return (0); +} + +static int +qoriq_clkgen_create_cmux(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + char namebuf[24]; + int i, error; + + sc = device_get_softc(dev); + + for (i = 0; i < sc->info->num_cmux; i++) { + sc->info->cmux_defs[i].clkdef.id = 100 + i; + sc->info->cmux_defs[i].clkdef.name = namebuf; + + snprintf(namebuf, 24, "cg-cmux-%d", i); + + error = clknode_mux_register(sc->clkdom, + &(sc->info->cmux_defs[i])); + sc->info->cmux_defs[i].clkdef.name = NULL; + if (error != 0) + return (error); + } + + return (0); +} + +static int +qoriq_clkgen_create_fman(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + struct clk_fixed_def def; + char namebuf[24]; + int i, error; + + sc = device_get_softc(dev); + memset(&def, 0, sizeof(struct clk_fixed_def)); + def.clkdef.name = namebuf; + def.clkdef.parent_cnt = 1; + def.div = 1; + def.mult = 1; + + for(i = 0; i < sc->info->num_fman; i++) { + snprintf(namebuf, 24, "cg-fman-%d", i); + def.clkdef.parent_names = &(sc->info->fman_src[i]); + def.clkdef.id = 300 + i; + + error = clknode_fixed_register(sc->clkdom, &def); + if (error != 0) + return (error); + } + + return (0); +} + +int +qoriq_clkgen_attach(device_t dev) +{ + struct qoriq_clkgen_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (sc->info == NULL) { + device_printf(dev, "No info struct given.\n"); + return (ENXIO); + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + error = bus_alloc_resources(dev, qoriq_clkgen_spec, &sc->res); + if (error != 0) { + device_printf(dev, "Could not allocate resources.\n"); + goto fail_early; + } + + sc->clkdom = clkdom_create(dev); + if (sc->clkdom == NULL) { + device_printf(dev, "Could not create clock domain.\n"); + error = ENXIO; + goto fail; + } + + error = qoriq_clkgen_create_sysclk(dev); + if (error != 0) { + device_printf(dev, "Could not create sysclk.\n"); + goto fail; + } + + error = qoriq_clkgen_create_coreclk(dev); + if (error != 0) + device_printf(dev, "Could not create coreclk.\n"); + + error = qoriq_clkgen_create_plls(dev); + if (error != 0) { + device_printf(dev, "Could not create PLL nodes.\n"); + goto fail; + } + + error = qoriq_clkgen_create_hwaccel(dev); + if (error != 0) { + device_printf(dev, "Could not create HWACCEL nodes.\n"); + goto fail; + } + + error = qoriq_clkgen_create_fman(dev); + if (error != 0) { + device_printf(dev, "Could not create FMAN nodes.\n"); + goto fail; + } + + error = qoriq_clkgen_create_cmux(dev); + if (error != 0) { + device_printf(dev, "Could not create CMUX nodes.\n"); + goto fail; + } + + error = clkdom_finit(sc->clkdom); + if (error != 0) { + device_printf(dev, "Could not finalize clock domain.\n"); + goto fail; + } + + clkdom_set_ofw_mapper(sc->clkdom, qoriq_clkgen_ofw_mapper); + + if (bootverbose) + clkdom_dump(sc->clkdom); + + return (0); + +fail: + bus_release_resources(dev, qoriq_clkgen_spec, &sc->res); +fail_early: + mtx_destroy(&sc->mtx); + return (error); +} Index: sys/conf/files.arm64 =================================================================== --- sys/conf/files.arm64 +++ sys/conf/files.arm64 @@ -193,6 +193,7 @@ arm64/intel/firmware.c optional soc_intel_stratix10 arm64/intel/stratix10-soc-fpga-mgr.c optional soc_intel_stratix10 arm64/intel/stratix10-svc.c optional soc_intel_stratix10 +arm64/qoriq/clk/qoriq_clkgen.c optional fdt qoriq_clk arm64/qualcomm/qcom_gcc.c optional qcom_gcc fdt contrib/vchiq/interface/compat/vchi_bsd.c optional vchiq soc_brcm_bcm2837 \ compile-with "${NORMAL_C} -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -I$S/contrib/vchiq"