Index: head/sys/arm/freescale/imx/imx_gpio.c =================================================================== --- head/sys/arm/freescale/imx/imx_gpio.c +++ head/sys/arm/freescale/imx/imx_gpio.c @@ -57,6 +57,14 @@ #include #include +#if defined(EXT_RESOURCES) && defined(__aarch64__) +#define IMX_ENABLE_CLOCKS +#endif + +#ifdef IMX_ENABLE_CLOCKS +#include +#endif + #include "gpio_if.h" #ifdef INTRNG @@ -119,13 +127,17 @@ #ifdef INTRNG struct gpio_irqsrc gpio_pic_irqsrc[NGPIO]; #endif +#ifdef IMX_ENABLE_CLOCKS + clk_t clk; +#endif }; static struct ofw_compat_data compat_data[] = { - {"fsl,imx6q-gpio", 1}, - {"fsl,imx53-gpio", 1}, - {"fsl,imx51-gpio", 1}, - {NULL, 0} + {"fsl,imx8mq-gpio", 1}, + {"fsl,imx6q-gpio", 1}, + {"fsl,imx53-gpio", 1}, + {"fsl,imx51-gpio", 1}, + {NULL, 0} }; static struct resource_spec imx_gpio_spec[] = { @@ -788,6 +800,9 @@ { struct imx51_gpio_softc *sc; int i, irq, unit; +#ifdef IMX_ENABLE_CLOCKS + int err; +#endif sc = device_get_softc(dev); sc->dev = dev; @@ -795,6 +810,19 @@ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->dev), NULL, MTX_SPIN); +#ifdef IMX_ENABLE_CLOCKS + if (clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk) != 0) { + device_printf(dev, "could not get clock"); + return (ENOENT); + } + + err = clk_enable(sc->clk); + if (err != 0) { + device_printf(sc->dev, "could not enable ipg clock\n"); + return (err); + } +#endif + if (bus_alloc_resources(dev, imx_gpio_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); bus_release_resources(dev, imx_gpio_spec, sc->sc_res); @@ -850,8 +878,19 @@ { int irq; struct imx51_gpio_softc *sc; +#ifdef IMX_ENABLE_CLOCKS + int error; +#endif sc = device_get_softc(dev); + +#ifdef IMX_ENABLE_CLOCKS + error = clk_disable(sc->clk); + if (error != 0) { + device_printf(sc->dev, "could not disable ipg clock\n"); + return (error); + } +#endif gpiobus_detach_bus(dev); for (irq = 0; irq < NUM_IRQRES; irq++) { Index: head/sys/arm/freescale/imx/imx_i2c.c =================================================================== --- head/sys/arm/freescale/imx/imx_i2c.c +++ head/sys/arm/freescale/imx/imx_i2c.c @@ -73,6 +73,14 @@ #include #include +#if defined(EXT_RESOURCES) && defined(__aarch64__) +#define IMX_ENABLE_CLOCKS +#endif + +#ifdef IMX_ENABLE_CLOCKS +#include +#endif + #define I2C_ADDR_REG 0x00 /* I2C slave address register */ #define I2C_FDR_REG 0x04 /* I2C frequency divider register */ #define I2C_CONTROL_REG 0x08 /* I2C control register */ @@ -125,6 +133,7 @@ }; static struct ofw_compat_data compat_data[] = { + {"fsl,imx21-i2c", 1}, {"fsl,imx6q-i2c", 1}, {"fsl,imx-i2c", 1}, {NULL, 0} @@ -141,6 +150,9 @@ gpio_pin_t rb_sdapin; u_int debug; u_int slave; +#ifdef IMX_ENABLE_CLOCKS + clk_t ipgclk; +#endif }; #define DEVICE_DEBUGF(sc, lvl, fmt, args...) \ @@ -385,6 +397,19 @@ sc->dev = dev; sc->rid = 0; +#ifdef IMX_ENABLE_CLOCKS + if (clk_get_by_ofw_index(sc->dev, 0, 0, &sc->ipgclk) != 0) { + device_printf(dev, "could not get ipg clock"); + return (ENOENT); + } + + err = clk_enable(sc->ipgclk); + if (err != 0) { + device_printf(sc->dev, "could not enable ipg clock\n"); + return (err); + } +#endif + sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid, RF_ACTIVE); if (sc->res == NULL) { @@ -459,6 +484,14 @@ sc = device_get_softc(dev); +#ifdef IMX_ENABLE_CLOCKS + error = clk_disable(sc->ipgclk); + if (error != 0) { + device_printf(sc->dev, "could not disable ipg clock\n"); + return (error); + } +#endif + if ((error = bus_generic_detach(sc->dev)) != 0) { device_printf(sc->dev, "cannot detach child devices\n"); return (error); @@ -571,6 +604,10 @@ { struct i2c_softc *sc; u_int busfreq, div, i, ipgfreq; +#ifdef IMX_ENABLE_CLOCKS + int err; + uint64_t freq; +#endif sc = device_get_softc(dev); @@ -580,7 +617,16 @@ * Look up the divisor that gives the nearest speed that doesn't exceed * the configured value for the bus. */ +#ifdef IMX_ENABLE_CLOCKS + err = clk_get_freq(sc->ipgclk, &freq); + if (err != 0) { + device_printf(sc->dev, "cannot get frequency\n"); + return (err); + } + ipgfreq = (int32_t)freq; +#else ipgfreq = imx_ccm_ipg_hz(); +#endif busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed); div = howmany(ipgfreq, busfreq); for (i = 0; i < nitems(clkdiv_table); i++) { Index: head/sys/arm/freescale/imx/imx_iomux.c =================================================================== --- head/sys/arm/freescale/imx/imx_iomux.c +++ head/sys/arm/freescale/imx/imx_iomux.c @@ -76,6 +76,7 @@ static struct iomux_softc *iomux_sc; static struct ofw_compat_data compat_data[] = { + {"fsl,imx8mq-iomuxc", true}, {"fsl,imx6dl-iomuxc", true}, {"fsl,imx6q-iomuxc", true}, {"fsl,imx6sl-iomuxc", true}, Index: head/sys/arm64/conf/GENERIC =================================================================== --- head/sys/arm64/conf/GENERIC +++ head/sys/arm64/conf/GENERIC @@ -113,6 +113,7 @@ options SOC_ALLWINNER_H5 options SOC_ALLWINNER_H6 options SOC_CAVM_THUNDERX +options SOC_FREESCALE_IMX8 options SOC_HISI_HI6220 options SOC_INTEL_STRATIX10 options SOC_BRCM_BCM2837 @@ -172,6 +173,7 @@ device dwc_rk # Rockchip Designware device dwc_socfpga # Altera SOCFPGA Ethernet MAC device genet # Broadcom on RPi4 +device ffec # iMX FFEC # Etherswitch devices device etherswitch # Enable etherswitch support @@ -205,6 +207,7 @@ # Serial (COM) ports device uart # Generic UART driver +device uart_imx # iMX8 UART device uart_msm # Qualcomm MSM UART driver device uart_mu # RPI3 aux port device uart_mvebu # Armada 3700 UART driver @@ -265,6 +268,7 @@ device syr827 # Silergy SYR827 PMIC device sy8106a # SY8106A Buck Regulator device vf_i2c # Freescale Vybrid I2C controller +device fsliic # Freescale iMX I2C controller # Clock and reset controllers device aw_ccu # Allwinner clock controller @@ -352,4 +356,4 @@ device acpi # DTBs -makeoptions MODULES_EXTRA="dtb/allwinner dtb/mv dtb/rockchip dtb/rpi" +makeoptions MODULES_EXTRA="dtb/allwinner dtb/imx8 dtb/mv dtb/rockchip dtb/rpi" Index: head/sys/arm64/freescale/imx/clk/imx_clk_composite.h =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_composite.h +++ head/sys/arm64/freescale/imx/clk/imx_clk_composite.h @@ -0,0 +1,45 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2018 Emmanuel Vadot + * + * 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 _IMX_CLK_COMPOSITE_H_ +#define _IMX_CLK_COMPOSITE_H_ + +#include + +struct imx_clk_composite_def { + struct clknode_init_def clkdef; + + uint32_t offset; + uint32_t flags; +}; + +int imx_clk_composite_register(struct clkdom *clkdom, + struct imx_clk_composite_def *clkdef); + +#endif /* _IMX_CLK_COMPOSITE_H_ */ Index: head/sys/arm64/freescale/imx/clk/imx_clk_composite.c =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_composite.c +++ head/sys/arm64/freescale/imx/clk/imx_clk_composite.c @@ -0,0 +1,309 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Emmanuel Vadot + * + * 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" + +#define TARGET_ROOT_ENABLE (1 << 28) +#define TARGET_ROOT_MUX(n) ((n) << 24) +#define TARGET_ROOT_MUX_MASK (7 << 24) +#define TARGET_ROOT_MUX_SHIFT 24 +#define TARGET_ROOT_PRE_PODF(n) ((((n) - 1) & 0x7) << 16) +#define TARGET_ROOT_PRE_PODF_MASK (0x7 << 16) +#define TARGET_ROOT_PRE_PODF_SHIFT 16 +#define TARGET_ROOT_PRE_PODF_MAX 7 +#define TARGET_ROOT_POST_PODF(n) ((((n) - 1) & 0x3f) << 0) +#define TARGET_ROOT_POST_PODF_MASK (0x3f << 0) +#define TARGET_ROOT_POST_PODF_SHIFT 0 +#define TARGET_ROOT_POST_PODF_MAX 0x3f + +struct imx_clk_composite_sc { + uint32_t offset; + 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 IMX_CLK_COMPOSITE_MASK_SHIFT 16 + +#if 0 +#define dprintf(format, arg...) \ + printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) +#else +#define dprintf(format, arg...) +#endif + +static int +imx_clk_composite_init(struct clknode *clk, device_t dev) +{ + struct imx_clk_composite_sc *sc; + uint32_t val, idx; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->offset, &val); + DEVICE_UNLOCK(clk); + idx = (val & TARGET_ROOT_MUX_MASK) >> TARGET_ROOT_MUX_SHIFT; + + clknode_init_parent_idx(clk, idx); + + return (0); +} + +static int +imx_clk_composite_set_gate(struct clknode *clk, bool enable) +{ + struct imx_clk_composite_sc *sc; + uint32_t val = 0; + + sc = clknode_get_softc(clk); + + dprintf("%sabling gate\n", enable ? "En" : "Dis"); + DEVICE_LOCK(clk); + READ4(clk, sc->offset, &val); + if (enable) + val |= TARGET_ROOT_ENABLE; + else + val &= ~(TARGET_ROOT_ENABLE); + WRITE4(clk, sc->offset, val); + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +imx_clk_composite_set_mux(struct clknode *clk, int index) +{ + struct imx_clk_composite_sc *sc; + uint32_t val = 0; + + sc = clknode_get_softc(clk); + + dprintf("Set mux to %d\n", index); + DEVICE_LOCK(clk); + READ4(clk, sc->offset, &val); + val &= ~(TARGET_ROOT_MUX_MASK); + val |= TARGET_ROOT_MUX(index); + WRITE4(clk, sc->offset, val); + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +imx_clk_composite_recalc(struct clknode *clk, uint64_t *freq) +{ + struct imx_clk_composite_sc *sc; + uint32_t reg, pre_div, post_div; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + + pre_div = ((reg & TARGET_ROOT_PRE_PODF_MASK) + >> TARGET_ROOT_PRE_PODF_SHIFT) + 1; + post_div = ((reg & TARGET_ROOT_POST_PODF_MASK) + >> TARGET_ROOT_POST_PODF_SHIFT) + 1; + + dprintf("parent_freq=%ju, div=%u\n", *freq, div); + *freq = *freq / pre_div / post_div; + dprintf("Final freq=%ju\n", *freq); + return (0); +} + +static int +imx_clk_composite_find_best(uint64_t fparent, uint64_t ftarget, + uint32_t *pre_div, uint32_t *post_div, int flags) +{ + uint32_t prediv, postdiv, best_prediv, best_postdiv; + int64_t diff, best_diff; + uint64_t cur; + + best_diff = INT64_MAX; + for (prediv = 1; prediv <= TARGET_ROOT_PRE_PODF_MAX + 1; prediv++) { + for (postdiv = 1; postdiv <= TARGET_ROOT_POST_PODF_MAX + 1; postdiv++) { + cur= fparent / prediv / postdiv; + diff = (int64_t)ftarget - (int64_t)cur; + if (flags & CLK_SET_ROUND_DOWN) { + if (diff >= 0 && diff < best_diff) { + best_diff = diff; + best_prediv = prediv; + best_postdiv = postdiv; + } + } + else if (flags & CLK_SET_ROUND_UP) { + if (diff <= 0 && abs(diff) < best_diff) { + best_diff = diff; + best_prediv = prediv; + best_postdiv = postdiv; + } + } + else { + if (abs(diff) < best_diff) { + best_diff = abs(diff); + best_prediv = prediv; + best_postdiv = postdiv; + } + } + } + } + + if (best_diff == INT64_MAX) + return (ERANGE); + + *pre_div = best_prediv; + *post_div = best_postdiv; + + return (0); +} + +static int +imx_clk_composite_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, + int flags, int *stop) +{ + struct imx_clk_composite_sc *sc; + struct clknode *p_clk; + const char **p_names; + int p_idx, best_parent; + int64_t best_diff, diff; + int32_t best_pre_div, best_post_div, pre_div, post_div; + uint64_t cur, best; + uint32_t val; + + sc = clknode_get_softc(clk); + dprintf("Finding best parent/div for target freq of %ju\n", *fout); + p_names = clknode_get_parent_names(clk); + + best_diff = 0; + + for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) { + p_clk = clknode_find_by_name(p_names[p_idx]); + clknode_get_freq(p_clk, &fparent); + dprintf("Testing with parent %s (%d) at freq %ju\n", + clknode_get_name(p_clk), p_idx, fparent); + + if (!imx_clk_composite_find_best(fparent, *fout, &pre_div, &post_div, sc->flags)) + continue; + cur = fparent / pre_div / post_div; + diff = abs((int64_t)*fout - (int64_t)cur); + if (diff < best_diff) { + best = cur; + best_diff = diff; + best_pre_div = pre_div; + best_post_div = pre_div; + best_parent = p_idx; + dprintf("Best parent so far %s (%d) with best freq at " + "%ju\n", clknode_get_name(p_clk), p_idx, best); + } + } + + *stop = 1; + if (best_diff == INT64_MAX) + return (ERANGE); + + if ((flags & CLK_SET_DRYRUN) != 0) { + *fout = best; + return (0); + } + + p_idx = clknode_get_parent_idx(clk); + if (p_idx != best_parent) { + dprintf("Switching parent index from %d to %d\n", p_idx, + best_parent); + clknode_set_parent_by_idx(clk, best_parent); + } + + dprintf("Setting dividers to pre=%d, post=%d\n", best_pre_div, best_post_div); + + DEVICE_LOCK(clk); + READ4(clk, sc->offset, &val); + val &= ~(TARGET_ROOT_PRE_PODF_MASK | TARGET_ROOT_POST_PODF_MASK); + val |= TARGET_ROOT_PRE_PODF(pre_div); + val |= TARGET_ROOT_POST_PODF(post_div); + DEVICE_UNLOCK(clk); + + *fout = best; + return (0); +} + +static clknode_method_t imx_clk_composite_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, imx_clk_composite_init), + CLKNODEMETHOD(clknode_set_gate, imx_clk_composite_set_gate), + CLKNODEMETHOD(clknode_set_mux, imx_clk_composite_set_mux), + CLKNODEMETHOD(clknode_recalc_freq, imx_clk_composite_recalc), + CLKNODEMETHOD(clknode_set_freq, imx_clk_composite_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(imx_clk_composite_clknode, imx_clk_composite_clknode_class, + imx_clk_composite_clknode_methods, sizeof(struct imx_clk_composite_sc), + clknode_class); + +int +imx_clk_composite_register(struct clkdom *clkdom, + struct imx_clk_composite_def *clkdef) +{ + struct clknode *clk; + struct imx_clk_composite_sc *sc; + + clk = clknode_create(clkdom, &imx_clk_composite_clknode_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + + sc->offset = clkdef->offset; + sc->flags = clkdef->flags; + + clknode_register(clkdom, clk); + + return (0); +} Index: head/sys/arm64/freescale/imx/clk/imx_clk_frac_pll.h =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_frac_pll.h +++ head/sys/arm64/freescale/imx/clk/imx_clk_frac_pll.h @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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 _IMX_CLK_FRAC_PLL_H_ +#define _IMX_CLK_FRAC_PLL_H_ + +#include + +struct imx_clk_frac_pll_def { + struct clknode_init_def clkdef; + uint32_t offset; +}; + +int imx_clk_frac_pll_register(struct clkdom *clkdom, struct imx_clk_frac_pll_def *clkdef); + +#endif /* _IMX_CLK_FRAC_PLL_H_ */ Index: head/sys/arm64/freescale/imx/clk/imx_clk_frac_pll.c =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_frac_pll.c +++ head/sys/arm64/freescale/imx/clk/imx_clk_frac_pll.c @@ -0,0 +1,177 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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 imx_clk_frac_pll_sc { + uint32_t offset; +}; + +#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 CFG0 0 +#define CFG0_PLL_LOCK (1 << 31) +#define CFG0_PD (1 << 19) +#define CFG0_BYPASS (1 << 14) +#define CFG0_NEWDIV_VAL (1 << 12) +#define CFG0_NEWDIV_ACK (1 << 11) +#define CFG0_OUTPUT_DIV_MASK (0x1f << 0) +#define CFG0_OUTPUT_DIV_SHIFT 0 +#define CFG1 4 +#define CFG1_FRAC_DIV_MASK (0xffffff << 7) +#define CFG1_FRAC_DIV_SHIFT 7 +#define CFG1_INT_DIV_MASK (0x7f << 0) +#define CFG1_INT_DIV_SHIFT 0 + +#if 0 +#define dprintf(format, arg...) \ + printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) +#else +#define dprintf(format, arg...) +#endif + +static int +imx_clk_frac_pll_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +imx_clk_frac_pll_set_gate(struct clknode *clk, bool enable) +{ + struct imx_clk_frac_pll_sc *sc; + uint32_t cfg0; + int timeout; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->offset + CFG0, &cfg0); + if (enable) + cfg0 &= ~(CFG0_PD); + else + cfg0 |= CFG0_PD; + WRITE4(clk, sc->offset + CFG0, cfg0); + + /* Wait for PLL to lock */ + if (enable && ((cfg0 & CFG0_BYPASS) == 0)) { + for (timeout = 1000; timeout; timeout--) { + READ4(clk, sc->offset + CFG0, &cfg0); + if (cfg0 & CFG0_PLL_LOCK) + break; + DELAY(1); + } + } + + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +imx_clk_frac_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct imx_clk_frac_pll_sc *sc; + uint32_t cfg0, cfg1; + uint64_t div, divfi, divff, divf_val; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->offset + CFG0, &cfg0); + READ4(clk, sc->offset + CFG1, &cfg1); + DEVICE_UNLOCK(clk); + + div = (cfg0 & CFG0_OUTPUT_DIV_MASK) >> CFG0_OUTPUT_DIV_SHIFT; + div = (div + 1) * 2; + divff = (cfg1 & CFG1_FRAC_DIV_MASK) >> CFG1_FRAC_DIV_SHIFT; + divfi = (cfg1 & CFG1_INT_DIV_MASK) >> CFG1_INT_DIV_SHIFT; + + /* PLL is bypassed */ + if (cfg0 & CFG0_BYPASS) + return (0); + + divf_val = 1 + divfi + (divff/0x1000000); + *freq = *freq * 8 * divf_val / div; + + return (0); +} + +static clknode_method_t imx_clk_frac_pll_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, imx_clk_frac_pll_init), + CLKNODEMETHOD(clknode_set_gate, imx_clk_frac_pll_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, imx_clk_frac_pll_recalc), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(imx_clk_frac_pll_clknode, imx_clk_frac_pll_clknode_class, + imx_clk_frac_pll_clknode_methods, sizeof(struct imx_clk_frac_pll_sc), + clknode_class); + +int +imx_clk_frac_pll_register(struct clkdom *clkdom, + struct imx_clk_frac_pll_def *clkdef) +{ + struct clknode *clk; + struct imx_clk_frac_pll_sc *sc; + + clk = clknode_create(clkdom, &imx_clk_frac_pll_clknode_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + + sc->offset = clkdef->offset; + + clknode_register(clkdom, clk); + + return (0); +} Index: head/sys/arm64/freescale/imx/clk/imx_clk_gate.h =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_gate.h +++ head/sys/arm64/freescale/imx/clk/imx_clk_gate.h @@ -0,0 +1,45 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2018 Emmanuel Vadot + * + * 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 _IMX_CLK_GATE_H_ +#define _IMX_CLK_GATE_H_ + +#include + +struct imx_clk_gate_def { + struct clknode_init_def clkdef; + uint32_t offset; + uint32_t shift; + uint32_t mask; + int gate_flags; +}; + +int imx_clk_gate_register(struct clkdom *clkdom, struct imx_clk_gate_def *clkdef); + +#endif /* _IMX_CLK_GATE_H_ */ Index: head/sys/arm64/freescale/imx/clk/imx_clk_gate.c =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_gate.c +++ head/sys/arm64/freescale/imx/clk/imx_clk_gate.c @@ -0,0 +1,117 @@ +/*- + * 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 imx_clk_gate_init(struct clknode *clk, device_t dev); +static int imx_clk_gate_set_gate(struct clknode *clk, bool enable); +struct imx_clk_gate_sc { + uint32_t offset; + uint32_t shift; + uint32_t mask; + int gate_flags; +}; + +static clknode_method_t imx_clk_gate_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, imx_clk_gate_init), + CLKNODEMETHOD(clknode_set_gate, imx_clk_gate_set_gate), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(imx_clk_gate, imx_clk_gate_class, imx_clk_gate_methods, + sizeof(struct imx_clk_gate_sc), clknode_class); + +static int +imx_clk_gate_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + return(0); +} + +static int +imx_clk_gate_set_gate(struct clknode *clk, bool enable) +{ + uint32_t reg; + struct imx_clk_gate_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + DEVICE_LOCK(clk); + rv = MD4(clk, sc->offset, sc->mask << sc->shift, + (enable ? sc->mask : 0) << sc->shift); + if (rv != 0) { + DEVICE_UNLOCK(clk); + return (rv); + } + RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + return(0); +} + +int +imx_clk_gate_register(struct clkdom *clkdom, struct imx_clk_gate_def *clkdef) +{ + struct clknode *clk; + struct imx_clk_gate_sc *sc; + + clk = clknode_create(clkdom, &imx_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->gate_flags = clkdef->gate_flags; + + clknode_register(clkdom, clk); + return (0); +} Index: head/sys/arm64/freescale/imx/clk/imx_clk_mux.h =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_mux.h +++ head/sys/arm64/freescale/imx/clk/imx_clk_mux.h @@ -0,0 +1,45 @@ +/*- + * 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 _IMX_CLK_MUX_H_ +#define _IMX_CLK_MUX_H_ + +#include + +struct imx_clk_mux_def { + struct clknode_init_def clkdef; + uint32_t offset; + uint32_t shift; + uint32_t width; + int mux_flags; +}; + +int imx_clk_mux_register(struct clkdom *clkdom, struct imx_clk_mux_def *clkdef); + +#endif /* _IMX_CLK_MUX_H_ */ Index: head/sys/arm64/freescale/imx/clk/imx_clk_mux.c =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_mux.c +++ head/sys/arm64/freescale/imx/clk/imx_clk_mux.c @@ -0,0 +1,138 @@ +/*- + * 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 "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 imx_clk_mux_init(struct clknode *clk, device_t dev); +static int imx_clk_mux_set_mux(struct clknode *clk, int idx); + +struct imx_clk_mux_sc { + uint32_t offset; + uint32_t shift; + uint32_t mask; + int mux_flags; +}; + +static clknode_method_t imx_clk_mux_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, imx_clk_mux_init), + CLKNODEMETHOD(clknode_set_mux, imx_clk_mux_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(imx_clk_mux, imx_clk_mux_class, imx_clk_mux_methods, + sizeof(struct imx_clk_mux_sc), clknode_class); + + +static int +imx_clk_mux_init(struct clknode *clk, device_t dev) +{ + uint32_t reg; + struct imx_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 +imx_clk_mux_set_mux(struct clknode *clk, int idx) +{ + uint32_t reg; + struct imx_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)); + if (rv != 0) { + DEVICE_UNLOCK(clk); + return (rv); + } + RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + + return(0); +} + +int +imx_clk_mux_register(struct clkdom *clkdom, struct imx_clk_mux_def *clkdef) +{ + struct clknode *clk; + struct imx_clk_mux_sc *sc; + + clk = clknode_create(clkdom, &imx_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); +} Index: head/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.h =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.h +++ head/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.h @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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 _IMX_CLK_SSCG_PLL_H_ +#define _IMX_CLK_SSCG_PLL_H_ + +#include + +struct imx_clk_sscg_pll_def { + struct clknode_init_def clkdef; + uint32_t offset; +}; + +int imx_clk_sscg_pll_register(struct clkdom *clkdom, struct imx_clk_sscg_pll_def *clkdef); + +#endif /* _IMX_CLK_SSCG_PLL_H_ */ Index: head/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.c =================================================================== --- head/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.c +++ head/sys/arm64/freescale/imx/clk/imx_clk_sscg_pll.c @@ -0,0 +1,195 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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 imx_clk_sscg_pll_sc { + uint32_t offset; +}; + +#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 CFG0 0x00 +#define CFG0_PLL_LOCK (1 << 31) +#define CFG0_PD (1 << 7) +#define CFG0_BYPASS2 (1 << 5) +#define CFG0_BYPASS1 (1 << 4) +#define CFG1 0x04 +#define CFG2 0x08 +#define CFG2_DIVR1_MASK (7 << 25) +#define CFG2_DIVR1_SHIFT 25 +#define CFG2_DIVR2_MASK (0x3f << 19) +#define CFG2_DIVR2_SHIFT 19 +#define CFG2_DIVF1_MASK (0x3f << 13) +#define CFG2_DIVF1_SHIFT 13 +#define CFG2_DIVF2_MASK (0x3f << 7) +#define CFG2_DIVF2_SHIFT 7 +#define CFG2_DIV_MASK (0x3f << 1) +#define CFG2_DIV_SHIFT 1 + +#if 0 +#define dprintf(format, arg...) \ + printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) +#else +#define dprintf(format, arg...) +#endif + +static int +imx_clk_sscg_pll_init(struct clknode *clk, device_t dev) +{ + struct imx_clk_sscg_pll_sc *sc; + + sc = clknode_get_softc(clk); + if (clknode_get_parents_num(clk) > 1) { + device_printf(clknode_get_device(clk), + "error: SSCG PLL does not support more than one parent yet\n"); + return (EINVAL); + } + clknode_init_parent_idx(clk, 0); + + return (0); +} + +static int +imx_clk_sscg_pll_set_gate(struct clknode *clk, bool enable) +{ + struct imx_clk_sscg_pll_sc *sc; + uint32_t cfg0; + int timeout; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->offset + CFG0, &cfg0); + if (enable) + cfg0 &= ~(CFG0_PD); + else + cfg0 |= CFG0_PD; + WRITE4(clk, sc->offset + CFG0, cfg0); + + /* Reading lock */ + if (enable) { + for (timeout = 1000; timeout; timeout--) { + READ4(clk, sc->offset + CFG0, &cfg0); + if (cfg0 & CFG0_PLL_LOCK) + break; + DELAY(1); + } + } + + DEVICE_UNLOCK(clk); + + return (0); +} + +static int +imx_clk_sscg_pll_recalc(struct clknode *clk, uint64_t *freq) +{ + struct imx_clk_sscg_pll_sc *sc; + uint32_t cfg0, cfg2; + int divr1, divr2, divf1, divf2, div; + + sc = clknode_get_softc(clk); + + DEVICE_LOCK(clk); + READ4(clk, sc->offset + CFG0, &cfg0); + READ4(clk, sc->offset + CFG2, &cfg2); + DEVICE_UNLOCK(clk); + + /* PLL is bypassed */ + if (cfg0 & CFG0_BYPASS2) + return (0); + + divr1 = (cfg2 & CFG2_DIVR1_MASK) >> CFG2_DIVR1_SHIFT; + divr2 = (cfg2 & CFG2_DIVR2_MASK) >> CFG2_DIVR2_SHIFT; + divf1 = (cfg2 & CFG2_DIVF1_MASK) >> CFG2_DIVF1_SHIFT; + divf2 = (cfg2 & CFG2_DIVF2_MASK) >> CFG2_DIVF2_SHIFT; + div = (cfg2 & CFG2_DIV_MASK) >> CFG2_DIV_SHIFT; + + if (cfg0 & CFG0_BYPASS1) { + *freq = *freq / ((divr2 + 1) * (div + 1)); + return (0); + } + + *freq *= 2 * (divf1 + 1) * (divf2 + 1); + *freq /= (divr1 + 1) * (divr2 + 1) * (div + 1); + + return (0); +} + +static clknode_method_t imx_clk_sscg_pll_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, imx_clk_sscg_pll_init), + CLKNODEMETHOD(clknode_set_gate, imx_clk_sscg_pll_set_gate), + CLKNODEMETHOD(clknode_recalc_freq, imx_clk_sscg_pll_recalc), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(imx_clk_sscg_pll_clknode, imx_clk_sscg_pll_clknode_class, + imx_clk_sscg_pll_clknode_methods, sizeof(struct imx_clk_sscg_pll_sc), + clknode_class); + +int +imx_clk_sscg_pll_register(struct clkdom *clkdom, + struct imx_clk_sscg_pll_def *clkdef) +{ + struct clknode *clk; + struct imx_clk_sscg_pll_sc *sc; + + clk = clknode_create(clkdom, &imx_clk_sscg_pll_clknode_class, + &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + + sc->offset = clkdef->offset; + + clknode_register(clkdom, clk); + + return (0); +} Index: head/sys/arm64/freescale/imx/imx7gpc.c =================================================================== --- head/sys/arm64/freescale/imx/imx7gpc.c +++ head/sys/arm64/freescale/imx/imx7gpc.c @@ -0,0 +1,263 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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 +#include + +#include +#include +#include + +#include "pic_if.h" + +struct imx7gpc_softc { + device_t dev; + struct resource *memres; + device_t parent; +}; + +static struct ofw_compat_data compat_data[] = { + { "fsl,imx7gpc", 1}, + { "fsl,imx8mq-gpc", 1}, + { NULL, 0} +}; + +static inline uint32_t +imx7gpc_read_4(struct imx7gpc_softc *sc, int reg) +{ + + return (bus_read_4(sc->memres, reg)); +} + +static inline void +imx7gpc_write_4(struct imx7gpc_softc *sc, int reg, uint32_t val) +{ + + bus_write_4(sc->memres, reg, val); +} + +static int +imx7gpc_activate_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); +} + +static void +imx7gpc_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + PIC_DISABLE_INTR(sc->parent, isrc); +} + +static void +imx7gpc_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + PIC_ENABLE_INTR(sc->parent, isrc); +} + +static int +imx7gpc_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + return (PIC_MAP_INTR(sc->parent, data, isrcp)); +} + +static int +imx7gpc_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data)); +} + +static int +imx7gpc_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); +} + +static int +imx7gpc_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); +} + +static void +imx7gpc_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + PIC_PRE_ITHREAD(sc->parent, isrc); +} + + +static void +imx7gpc_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + PIC_POST_ITHREAD(sc->parent, isrc); +} + +static void +imx7gpc_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + PIC_POST_FILTER(sc->parent, isrc); +} + +#ifdef SMP +static int +imx7gpc_bind_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + + return (PIC_BIND_INTR(sc->parent, isrc)); +} +#endif + +static int +imx7gpc_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "General Power Controller"); + return (BUS_PROBE_DEFAULT); +} + +static int +imx7gpc_attach(device_t dev) +{ + struct imx7gpc_softc *sc = device_get_softc(dev); + phandle_t node; + phandle_t parent_xref; + int i, rv; + + sc->dev = dev; + + node = ofw_bus_get_node(dev); + + rv = OF_getencprop(node, "interrupt-parent", &parent_xref, + sizeof(parent_xref)); + if (rv <= 0) { + device_printf(dev, "Can't read parent node property\n"); + return (ENXIO); + } + sc->parent = OF_device_from_xref(parent_xref); + if (sc->parent == NULL) { + device_printf(dev, "Can't find parent controller\n"); + return (ENXIO); + } + + + i = 0; + sc->memres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, + RF_ACTIVE); + if (sc->memres == NULL) { + device_printf(dev, "could not allocate resources\n"); + return (ENXIO); + } + + /* TODO: power up OTG domain and unmask all interrupts */ + + if (intr_pic_register(dev, OF_xref_from_node(node)) == NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, i, sc->memres); + device_printf(dev, "Cannot register PIC\n"); + return (ENXIO); + } + + return (0); +} + +static device_method_t imx7gpc_methods[] = { + DEVMETHOD(device_probe, imx7gpc_probe), + DEVMETHOD(device_attach, imx7gpc_attach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_activate_intr, imx7gpc_activate_intr), + DEVMETHOD(pic_disable_intr, imx7gpc_disable_intr), + DEVMETHOD(pic_enable_intr, imx7gpc_enable_intr), + DEVMETHOD(pic_map_intr, imx7gpc_map_intr), + DEVMETHOD(pic_deactivate_intr, imx7gpc_deactivate_intr), + DEVMETHOD(pic_setup_intr, imx7gpc_setup_intr), + DEVMETHOD(pic_teardown_intr, imx7gpc_teardown_intr), + DEVMETHOD(pic_pre_ithread, imx7gpc_pre_ithread), + DEVMETHOD(pic_post_ithread, imx7gpc_post_ithread), + DEVMETHOD(pic_post_filter, imx7gpc_post_filter), +#ifdef SMP + DEVMETHOD(pic_bind_intr, imx7gpc_bind_intr), +#endif + + DEVMETHOD_END +}; + +static driver_t imx7gpc_driver = { + "imx7gpc", + imx7gpc_methods, + sizeof(struct imx7gpc_softc), +}; + +static devclass_t imx7gpc_devclass; + +EARLY_DRIVER_MODULE(imx7gpc, ofwbus, imx7gpc_driver, imx7gpc_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); +EARLY_DRIVER_MODULE(imx7gpc, simplebus, imx7gpc_driver, imx7gpc_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); Index: head/sys/arm64/freescale/imx/imx8mq_ccm.h =================================================================== --- head/sys/arm64/freescale/imx/imx8mq_ccm.h +++ head/sys/arm64/freescale/imx/imx8mq_ccm.h @@ -0,0 +1,173 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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 __IMX8MQ_CCM_H__ +#define __IMX8MQ_CCM_H__ + +#define IMX8MQ_CLK_DUMMY 0 +#define IMX8MQ_CLK_32K 1 +#define IMX8MQ_CLK_25M 2 +#define IMX8MQ_CLK_27M 3 +#define IMX8MQ_CLK_EXT1 4 +#define IMX8MQ_CLK_EXT2 5 +#define IMX8MQ_CLK_EXT3 6 +#define IMX8MQ_CLK_EXT4 7 + +#define IMX8MQ_ARM_PLL_REF_SEL 8 +#define IMX8MQ_ARM_PLL_REF_DIV 9 +#define IMX8MQ_ARM_PLL 10 +#define IMX8MQ_ARM_PLL_BYPASS 11 +#define IMX8MQ_ARM_PLL_OUT 12 + +#define IMX8MQ_GPU_PLL_REF_SEL 13 +#define IMX8MQ_GPU_PLL_REF_DIV 14 +#define IMX8MQ_GPU_PLL 15 +#define IMX8MQ_GPU_PLL_BYPASS 16 +#define IMX8MQ_GPU_PLL_OUT 17 + +#define IMX8MQ_VPU_PLL_REF_SEL 18 +#define IMX8MQ_VPU_PLL_REF_DIV 19 +#define IMX8MQ_VPU_PLL 20 +#define IMX8MQ_VPU_PLL_BYPASS 21 +#define IMX8MQ_VPU_PLL_OUT 22 + +#define IMX8MQ_AUDIO_PLL1_REF_SEL 23 +#define IMX8MQ_AUDIO_PLL1_REF_DIV 24 +#define IMX8MQ_AUDIO_PLL1 25 +#define IMX8MQ_AUDIO_PLL1_BYPASS 26 +#define IMX8MQ_AUDIO_PLL1_OUT 27 + +#define IMX8MQ_AUDIO_PLL2_REF_SEL 28 +#define IMX8MQ_AUDIO_PLL2_REF_DIV 29 +#define IMX8MQ_AUDIO_PLL2 30 +#define IMX8MQ_AUDIO_PLL2_BYPASS 31 +#define IMX8MQ_AUDIO_PLL2_OUT 32 + +#define IMX8MQ_VIDEO_PLL1_REF_SEL 33 +#define IMX8MQ_VIDEO_PLL1_REF_DIV 34 +#define IMX8MQ_VIDEO_PLL1 35 +#define IMX8MQ_VIDEO_PLL1_BYPASS 36 +#define IMX8MQ_VIDEO_PLL1_OUT 37 + +#define IMX8MQ_SYS3_PLL1_REF_SEL 54 +#define IMX8MQ_SYS3_PLL1 56 + +#define IMX8MQ_DRAM_PLL1_REF_SEL 62 + +#define IMX8MQ_SYS1_PLL_40M 70 +#define IMX8MQ_SYS1_PLL_80M 71 +#define IMX8MQ_SYS1_PLL_100M 72 +#define IMX8MQ_SYS1_PLL_133M 73 +#define IMX8MQ_SYS1_PLL_160M 74 +#define IMX8MQ_SYS1_PLL_200M 75 +#define IMX8MQ_SYS1_PLL_266M 76 +#define IMX8MQ_SYS1_PLL_400M 77 +#define IMX8MQ_SYS1_PLL_800M 78 + +#define IMX8MQ_SYS2_PLL_50M 79 +#define IMX8MQ_SYS2_PLL_100M 80 +#define IMX8MQ_SYS2_PLL_125M 81 +#define IMX8MQ_SYS2_PLL_166M 82 +#define IMX8MQ_SYS2_PLL_200M 83 +#define IMX8MQ_SYS2_PLL_250M 84 +#define IMX8MQ_SYS2_PLL_333M 85 +#define IMX8MQ_SYS2_PLL_500M 86 +#define IMX8MQ_SYS2_PLL_1000M 87 + +#define IMX8MQ_CLK_ENET_AXI 104 +#define IMX8MQ_CLK_USB_BUS 110 + +#define IMX8MQ_CLK_AHB 116 + +#define IMX8MQ_CLK_ENET_REF 137 +#define IMX8MQ_CLK_ENET_TIMER 138 +#define IMX8MQ_CLK_ENET_PHY_REF 139 +#define IMX8MQ_CLK_USDHC1 142 +#define IMX8MQ_CLK_USDHC2 143 +#define IMX8MQ_CLK_I2C1 144 +#define IMX8MQ_CLK_I2C2 145 +#define IMX8MQ_CLK_I2C3 146 +#define IMX8MQ_CLK_I2C4 147 +#define IMX8MQ_CLK_UART1 148 +#define IMX8MQ_CLK_UART2 149 +#define IMX8MQ_CLK_UART3 150 +#define IMX8MQ_CLK_UART4 151 +#define IMX8MQ_CLK_USB_CORE_REF 152 +#define IMX8MQ_CLK_USB_PHY_REF 153 + +#define IMX8MQ_CLK_ENET1_ROOT 182 +#define IMX8MQ_CLK_I2C1_ROOT 184 +#define IMX8MQ_CLK_I2C2_ROOT 185 +#define IMX8MQ_CLK_I2C3_ROOT 186 +#define IMX8MQ_CLK_I2C4_ROOT 187 +#define IMX8MQ_CLK_UART1_ROOT 202 +#define IMX8MQ_CLK_UART2_ROOT 203 +#define IMX8MQ_CLK_UART3_ROOT 204 +#define IMX8MQ_CLK_UART4_ROOT 205 +#define IMX8MQ_CLK_USB1_CTRL_ROOT 206 +#define IMX8MQ_CLK_USB2_CTRL_ROOT 207 +#define IMX8MQ_CLK_USB1_PHY_ROOT 208 +#define IMX8MQ_CLK_USB2_PHY_ROOT 209 +#define IMX8MQ_CLK_USDHC1_ROOT 210 +#define IMX8MQ_CLK_USDHC2_ROOT 211 + +#define IMX8MQ_SYS1_PLL_OUT 231 +#define IMX8MQ_SYS2_PLL_OUT 232 +#define IMX8MQ_SYS3_PLL_OUT 233 + +#define IMX8MQ_CLK_IPG_ROOT 236 + +#define IMX8MQ_CLK_GPIO1_ROOT 259 +#define IMX8MQ_CLK_GPIO2_ROOT 260 +#define IMX8MQ_CLK_GPIO3_ROOT 261 +#define IMX8MQ_CLK_GPIO4_ROOT 262 +#define IMX8MQ_CLK_GPIO5_ROOT 263 + +#define IMX8MQ_VIDEO2_PLL1_REF_SEL 266 + +#define IMX8MQ_SYS1_PLL_40M_CG 267 +#define IMX8MQ_SYS1_PLL_80M_CG 268 +#define IMX8MQ_SYS1_PLL_100M_CG 269 +#define IMX8MQ_SYS1_PLL_133M_CG 270 +#define IMX8MQ_SYS1_PLL_160M_CG 271 +#define IMX8MQ_SYS1_PLL_200M_CG 272 +#define IMX8MQ_SYS1_PLL_266M_CG 273 +#define IMX8MQ_SYS1_PLL_400M_CG 274 +#define IMX8MQ_SYS1_PLL_800M_CG 275 +#define IMX8MQ_SYS2_PLL_50M_CG 276 +#define IMX8MQ_SYS2_PLL_100M_CG 277 +#define IMX8MQ_SYS2_PLL_125M_CG 278 +#define IMX8MQ_SYS2_PLL_166M_CG 279 +#define IMX8MQ_SYS2_PLL_200M_CG 280 +#define IMX8MQ_SYS2_PLL_250M_CG 281 +#define IMX8MQ_SYS2_PLL_333M_CG 282 +#define IMX8MQ_SYS2_PLL_500M_CG 283 +#define IMX8MQ_SYS2_PLL_1000M_CG 284 + +#endif Index: head/sys/arm64/freescale/imx/imx8mq_ccm.c =================================================================== --- head/sys/arm64/freescale/imx/imx8mq_ccm.c +++ head/sys/arm64/freescale/imx/imx8mq_ccm.c @@ -0,0 +1,484 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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$"); + +/* + * Clocks driver for Freescale i.MX8MQ SoC + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "clkdev_if.h" + +static const char *pll_ref_p[] = { + "osc_25m", "osc_27m", "dummy", "dummy" +}; +static const char *sys3_pll_out_p[] = { + "sys3_pll1_ref_sel" +}; +static const char * arm_pll_bypass_p[] = { + "arm_pll", "arm_pll_ref_sel" +}; +static const char * gpu_pll_bypass_p[] = { + "gpu_pll", "gpu_pll_ref_sel" +}; +static const char * vpu_pll_bypass_p[] = { + "vpu_pll", "vpu_pll_ref_sel" +}; +static const char * audio_pll1_bypass_p[] = { + "audio_pll1", "audio_pll1_ref_sel" +}; +static const char * audio_pll2_bypass_p[] = { + "audio_pll2", "audio_pll2_ref_sel" +}; +static const char * video_pll1_bypass_p[] = { + "video_pll1", "video_pll1_ref_sel" +}; +static const char *uart_p[] = { + "osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", "sys3_pll_out", + "clk_ext2", "clk_ext4", "audio_pll2_out" +}; +static const char *usdhc_p[] = { + "osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", "audio_pll2_out", + "sys1_pll_266m", "sys3_pll_out", "sys1_pll_100m" +}; +static const char *enet_axi_p[] = { + "osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_250m", "sys2_pll_200m", + "audio_pll1_out", "video_pll1_out", "sys3_pll_out" +}; +static const char *enet_ref_p[] = { + "osc_25m", "sys2_pll_125m", "sys2_pll_500m", "sys2_pll_100m", "sys1_pll_160m", + "audio_pll1_out", "video_pll1_out", "clk_ext4" +}; +static const char *enet_timer_p[] = { + "osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", "clk_ext3", + "clk_ext4", "video_pll1_out" +}; +static const char *enet_phy_ref_p[] = { + "osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out" +}; +static const char *usb_bus_p[] = { + "osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m", "sys2_pll_200m", + "clk_ext2", "clk_ext4", "audio_pll2_out" +}; +static const char *usb_core_phy_p[] = { + "osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", "sys2_pll_200m", + "clk_ext2", "clk_ext3", "audio_pll2_out" +}; +static const char *i2c_p[] = { + "osc_25m", "sys1_pll_160m", "sys2_pll_50m", "sys3_pll_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys1_pll_133m" +}; +static const char *ahb_p[] = { + "osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m", "sys2_pll_125m", + "sys3_pll_out", "audio_pll1_out", "video_pll1_out" +}; + +static struct imx_clk imx_clks[] = { + FIXED(IMX8MQ_CLK_DUMMY, "dummy", 0), + + LINK(IMX8MQ_CLK_32K, "ckil"), + LINK(IMX8MQ_CLK_25M, "osc_25m"), + LINK(IMX8MQ_CLK_27M, "osc_27m"), + LINK(IMX8MQ_CLK_EXT1, "clk_ext1"), + LINK(IMX8MQ_CLK_EXT2, "clk_ext2"), + LINK(IMX8MQ_CLK_EXT3, "clk_ext3"), + LINK(IMX8MQ_CLK_EXT4, "clk_ext4"), + + FIXED(IMX8MQ_SYS1_PLL_OUT, "sys1_pll_out", 800000000), + FIXED(IMX8MQ_SYS2_PLL_OUT, "sys2_pll_out", 1000000000), + SSCG_PLL(IMX8MQ_SYS3_PLL_OUT, "sys3_pll_out", sys3_pll_out_p, 0x48), + + MUX(IMX8MQ_ARM_PLL_REF_SEL, "arm_pll_ref_sel", pll_ref_p, 0, 0x28, 16, 2), + MUX(IMX8MQ_GPU_PLL_REF_SEL, "gpu_pll_ref_sel", pll_ref_p, 0, 0x18, 16, 2), + MUX(IMX8MQ_VPU_PLL_REF_SEL, "vpu_pll_ref_sel", pll_ref_p, 0, 0x20, 16, 2), + MUX(IMX8MQ_AUDIO_PLL1_REF_SEL, "audio_pll1_ref_sel", pll_ref_p, 0, 0x0, 16, 2), + MUX(IMX8MQ_AUDIO_PLL2_REF_SEL, "audio_pll2_ref_sel", pll_ref_p, 0, 0x8, 16, 2), + MUX(IMX8MQ_VIDEO_PLL1_REF_SEL, "video_pll1_ref_sel", pll_ref_p, 0, 0x10, 16, 2), + MUX(IMX8MQ_SYS3_PLL1_REF_SEL, "sys3_pll1_ref_sel", pll_ref_p, 0, 0x48, 0, 2), + MUX(IMX8MQ_DRAM_PLL1_REF_SEL, "dram_pll1_ref_sel", pll_ref_p, 0, 0x60, 0, 2), + MUX(IMX8MQ_VIDEO2_PLL1_REF_SEL, "video2_pll1_ref_sel", pll_ref_p, 0, 0x54, 0, 2), + + DIV(IMX8MQ_ARM_PLL_REF_DIV, "arm_pll_ref_div", "arm_pll_ref_sel", 0x28, 5, 6), + DIV(IMX8MQ_GPU_PLL_REF_DIV, "gpu_pll_ref_div", "gpu_pll_ref_sel", 0x18, 5, 6), + DIV(IMX8MQ_VPU_PLL_REF_DIV, "vpu_pll_ref_div", "vpu_pll_ref_sel", 0x20, 5, 6), + DIV(IMX8MQ_AUDIO_PLL1_REF_DIV, "audio_pll1_ref_div", "audio_pll1_ref_sel", 0x0, 5, 6), + DIV(IMX8MQ_AUDIO_PLL2_REF_DIV, "audio_pll2_ref_div", "audio_pll2_ref_sel", 0x8, 5, 6), + DIV(IMX8MQ_VIDEO_PLL1_REF_DIV, "video_pll1_ref_div", "video_pll1_ref_sel", 0x10, 5, 6), + + FRAC_PLL(IMX8MQ_ARM_PLL, "arm_pll", "arm_pll_ref_div", 0x28), + FRAC_PLL(IMX8MQ_GPU_PLL, "gpu_pll", "gpu_pll_ref_div", 0x18), + FRAC_PLL(IMX8MQ_VPU_PLL, "vpu_pll", "vpu_pll_ref_div", 0x20), + FRAC_PLL(IMX8MQ_AUDIO_PLL1, "audio_pll1", "audio_pll1_ref_div", 0x0), + FRAC_PLL(IMX8MQ_AUDIO_PLL2, "audio_pll2", "audio_pll2_ref_div", 0x8), + FRAC_PLL(IMX8MQ_VIDEO_PLL1, "video_pll1", "video_pll1_ref_div", 0x10), + + /* ARM_PLL needs SET_PARENT flag */ + MUX(IMX8MQ_ARM_PLL_BYPASS, "arm_pll_bypass", arm_pll_bypass_p, 0, 0x28, 14, 1), + MUX(IMX8MQ_GPU_PLL_BYPASS, "gpu_pll_bypass", gpu_pll_bypass_p, 0, 0x18, 14, 1), + MUX(IMX8MQ_VPU_PLL_BYPASS, "vpu_pll_bypass", vpu_pll_bypass_p, 0, 0x20, 14, 1), + MUX(IMX8MQ_AUDIO_PLL1_BYPASS, "audio_pll1_bypass", audio_pll1_bypass_p, 0, 0x0, 14, 1), + MUX(IMX8MQ_AUDIO_PLL2_BYPASS, "audio_pll2_bypass", audio_pll2_bypass_p, 0, 0x8, 14, 1), + MUX(IMX8MQ_VIDEO_PLL1_BYPASS, "video_pll1_bypass", video_pll1_bypass_p, 0, 0x10, 14, 1), + + GATE(IMX8MQ_ARM_PLL_OUT, "arm_pll_out", "arm_pll_bypass", 0x28, 21), + GATE(IMX8MQ_GPU_PLL_OUT, "gpu_pll_out", "gpu_pll_bypass", 0x18, 21), + GATE(IMX8MQ_VPU_PLL_OUT, "vpu_pll_out", "vpu_pll_bypass", 0x20, 21), + GATE(IMX8MQ_AUDIO_PLL1_OUT, "audio_pll1_out", "audio_pll1_bypass", 0x0, 21), + GATE(IMX8MQ_AUDIO_PLL2_OUT, "audio_pll2_out", "audio_pll2_bypass", 0x8, 21), + GATE(IMX8MQ_VIDEO_PLL1_OUT, "video_pll1_out", "video_pll1_bypass", 0x10, 21), + + GATE(IMX8MQ_SYS1_PLL_40M_CG, "sys1_pll_40m_cg", "sys1_pll_out", 0x30, 9), + GATE(IMX8MQ_SYS1_PLL_80M_CG, "sys1_pll_80m_cg", "sys1_pll_out", 0x30, 11), + GATE(IMX8MQ_SYS1_PLL_100M_CG, "sys1_pll_100m_cg", "sys1_pll_out", 0x30, 13), + GATE(IMX8MQ_SYS1_PLL_133M_CG, "sys1_pll_133m_cg", "sys1_pll_out", 0x30, 15), + GATE(IMX8MQ_SYS1_PLL_160M_CG, "sys1_pll_160m_cg", "sys1_pll_out", 0x30, 17), + GATE(IMX8MQ_SYS1_PLL_200M_CG, "sys1_pll_200m_cg", "sys1_pll_out", 0x30, 19), + GATE(IMX8MQ_SYS1_PLL_266M_CG, "sys1_pll_266m_cg", "sys1_pll_out", 0x30, 21), + GATE(IMX8MQ_SYS1_PLL_400M_CG, "sys1_pll_400m_cg", "sys1_pll_out", 0x30, 23), + GATE(IMX8MQ_SYS1_PLL_800M_CG, "sys1_pll_800m_cg", "sys1_pll_out", 0x30, 25), + + FFACT(IMX8MQ_SYS1_PLL_40M, "sys1_pll_40m", "sys1_pll_40m_cg", 1, 20), + FFACT(IMX8MQ_SYS1_PLL_80M, "sys1_pll_80m", "sys1_pll_80m_cg", 1, 10), + FFACT(IMX8MQ_SYS1_PLL_100M, "sys1_pll_100m", "sys1_pll_100m_cg", 1, 8), + FFACT(IMX8MQ_SYS1_PLL_133M, "sys1_pll_133m", "sys1_pll_133m_cg", 1, 6), + FFACT(IMX8MQ_SYS1_PLL_160M, "sys1_pll_160m", "sys1_pll_160m_cg", 1, 5), + FFACT(IMX8MQ_SYS1_PLL_200M, "sys1_pll_200m", "sys1_pll_200m_cg", 1, 4), + FFACT(IMX8MQ_SYS1_PLL_266M, "sys1_pll_266m", "sys1_pll_266m_cg", 1, 3), + FFACT(IMX8MQ_SYS1_PLL_400M, "sys1_pll_400m", "sys1_pll_400m_cg", 1, 2), + FFACT(IMX8MQ_SYS1_PLL_800M, "sys1_pll_800m", "sys1_pll_800m_cg", 1, 1), + + GATE(IMX8MQ_SYS2_PLL_50M_CG, "sys2_pll_50m_cg", "sys2_pll_out", 0x3c, 9), + GATE(IMX8MQ_SYS2_PLL_100M_CG, "sys2_pll_100m_cg", "sys2_pll_out", 0x3c, 11), + GATE(IMX8MQ_SYS2_PLL_125M_CG, "sys2_pll_125m_cg", "sys2_pll_out", 0x3c, 13), + GATE(IMX8MQ_SYS2_PLL_166M_CG, "sys2_pll_166m_cg", "sys2_pll_out", 0x3c, 15), + GATE(IMX8MQ_SYS2_PLL_200M_CG, "sys2_pll_200m_cg", "sys2_pll_out", 0x3c, 17), + GATE(IMX8MQ_SYS2_PLL_250M_CG, "sys2_pll_250m_cg", "sys2_pll_out", 0x3c, 19), + GATE(IMX8MQ_SYS2_PLL_333M_CG, "sys2_pll_333m_cg", "sys2_pll_out", 0x3c, 21), + GATE(IMX8MQ_SYS2_PLL_500M_CG, "sys2_pll_500m_cg", "sys2_pll_out", 0x3c, 23), + GATE(IMX8MQ_SYS2_PLL_1000M_CG, "sys2_pll_1000m_cg", "sys2_pll_out", 0x3c, 25), + + FFACT(IMX8MQ_SYS2_PLL_50M, "sys2_pll_50m", "sys2_pll_50m_cg", 1, 20), + FFACT(IMX8MQ_SYS2_PLL_100M, "sys2_pll_100m", "sys2_pll_100m_cg", 1, 10), + FFACT(IMX8MQ_SYS2_PLL_125M, "sys2_pll_125m", "sys2_pll_125m_cg", 1, 8), + FFACT(IMX8MQ_SYS2_PLL_166M, "sys2_pll_166m", "sys2_pll_166m_cg", 1, 6), + FFACT(IMX8MQ_SYS2_PLL_200M, "sys2_pll_200m", "sys2_pll_200m_cg", 1, 5), + FFACT(IMX8MQ_SYS2_PLL_250M, "sys2_pll_250m", "sys2_pll_250m_cg", 1, 4), + FFACT(IMX8MQ_SYS2_PLL_333M, "sys2_pll_333m", "sys2_pll_333m_cg", 1, 3), + FFACT(IMX8MQ_SYS2_PLL_500M, "sys2_pll_500m", "sys2_pll_500m_cg", 1, 2), + FFACT(IMX8MQ_SYS2_PLL_1000M, "sys2_pll_1000m", "sys2_pll_1000m_cg", 1, 1), + + COMPOSITE(IMX8MQ_CLK_AHB, "ahb", ahb_p, 0x9000, 0), + DIV(IMX8MQ_CLK_IPG_ROOT, "ipg_root", "ahb", 0x9080, 0, 1), + + COMPOSITE(IMX8MQ_CLK_UART1, "uart1", uart_p, 0xaf00, 0), + COMPOSITE(IMX8MQ_CLK_UART2, "uart2", uart_p, 0xaf80, 0), + COMPOSITE(IMX8MQ_CLK_UART3, "uart3", uart_p, 0xb000, 0), + COMPOSITE(IMX8MQ_CLK_UART4, "uart4", uart_p, 0xb080, 0), + + ROOT_GATE(IMX8MQ_CLK_UART1_ROOT, "uart1_root_clk", "uart1", 0x4490), + ROOT_GATE(IMX8MQ_CLK_UART2_ROOT, "uart2_root_clk", "uart2", 0x44a0), + ROOT_GATE(IMX8MQ_CLK_UART3_ROOT, "uart3_root_clk", "uart3", 0x44b0), + ROOT_GATE(IMX8MQ_CLK_UART4_ROOT, "uart4_root_clk", "uart4", 0x44c0), + + COMPOSITE(IMX8MQ_CLK_USDHC1, "usdhc1", usdhc_p, 0xac00, CLK_SET_ROUND_DOWN), + COMPOSITE(IMX8MQ_CLK_USDHC2, "usdhc2", usdhc_p, 0xac80, CLK_SET_ROUND_DOWN), + + ROOT_GATE(IMX8MQ_CLK_USDHC1_ROOT, "usdhc1_root_clk", "usdhc1", 0x4510), + ROOT_GATE(IMX8MQ_CLK_USDHC2_ROOT, "usdhc2_root_clk", "usdhc2", 0x4520), + + COMPOSITE(IMX8MQ_CLK_ENET_AXI, "enet_axi", enet_axi_p, 0x8800, 0), + COMPOSITE(IMX8MQ_CLK_ENET_REF, "enet_ref", enet_ref_p, 0xa980, 0), + COMPOSITE(IMX8MQ_CLK_ENET_TIMER, "enet_timer", enet_timer_p, 0xaa00, 0), + COMPOSITE(IMX8MQ_CLK_ENET_PHY_REF, "enet_phy_ref", enet_phy_ref_p, 0xaa80, 0), + + ROOT_GATE(IMX8MQ_CLK_ENET1_ROOT, "enet1_root_clk", "enet_axi", 0x40a0), + + COMPOSITE(IMX8MQ_CLK_USB_BUS, "usb_bus", usb_bus_p, 0x8b80, 0), + COMPOSITE(IMX8MQ_CLK_USB_CORE_REF, "usb_core_ref", usb_core_phy_p, 0xb100, 0), + COMPOSITE(IMX8MQ_CLK_USB_PHY_REF, "usb_phy_ref", usb_core_phy_p, 0xb180, 0), + + ROOT_GATE(IMX8MQ_CLK_USB1_CTRL_ROOT, "usb1_ctrl_root_clk", "usb_bus", 0x44d0), + ROOT_GATE(IMX8MQ_CLK_USB2_CTRL_ROOT, "usb2_ctrl_root_clk", "usb_bus", 0x44e0), + ROOT_GATE(IMX8MQ_CLK_USB1_PHY_ROOT, "usb1_phy_root_clk", "usb_phy_ref", 0x44f0), + ROOT_GATE(IMX8MQ_CLK_USB2_PHY_ROOT, "usb2_phy_root_clk", "usb_phy_ref", 0x4500), + + COMPOSITE(IMX8MQ_CLK_I2C1, "i2c1", i2c_p, 0xad00, 0), + COMPOSITE(IMX8MQ_CLK_I2C2, "i2c2", i2c_p, 0xad80, 0), + COMPOSITE(IMX8MQ_CLK_I2C3, "i2c3", i2c_p, 0xae00, 0), + COMPOSITE(IMX8MQ_CLK_I2C4, "i2c4", i2c_p, 0xae80, 0), + + ROOT_GATE(IMX8MQ_CLK_I2C1_ROOT, "i2c1_root_clk", "i2c1", 0x4170), + ROOT_GATE(IMX8MQ_CLK_I2C2_ROOT, "i2c2_root_clk", "i2c2", 0x4180), + ROOT_GATE(IMX8MQ_CLK_I2C3_ROOT, "i2c3_root_clk", "i2c3", 0x4190), + ROOT_GATE(IMX8MQ_CLK_I2C4_ROOT, "i2c4_root_clk", "i2c4", 0x41a0), + + ROOT_GATE(IMX8MQ_CLK_GPIO1_ROOT, "gpio1_root_clk", "ipg_root", 0x40b0), + ROOT_GATE(IMX8MQ_CLK_GPIO2_ROOT, "gpio2_root_clk", "ipg_root", 0x40c0), + ROOT_GATE(IMX8MQ_CLK_GPIO3_ROOT, "gpio3_root_clk", "ipg_root", 0x40d0), + ROOT_GATE(IMX8MQ_CLK_GPIO4_ROOT, "gpio4_root_clk", "ipg_root", 0x40e0), + ROOT_GATE(IMX8MQ_CLK_GPIO5_ROOT, "gpio5_root_clk", "ipg_root", 0x40f0), +}; + +struct ccm_softc { + device_t dev; + struct resource *mem_res; + struct clkdom *clkdom; + struct mtx mtx; + struct imx_clk *clks; + int nclks; +}; + +static inline uint32_t +CCU_READ4(struct ccm_softc *sc, bus_size_t off) +{ + + return (bus_read_4(sc->mem_res, off)); +} + +static inline void +CCU_WRITE4(struct ccm_softc *sc, bus_size_t off, uint32_t val) +{ + + bus_write_4(sc->mem_res, off, val); +} + +static int +ccm_detach(device_t dev) +{ + struct ccm_softc *sc; + + sc = device_get_softc(dev); + + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + + return (0); +} + +static int +ccm_attach(device_t dev) +{ + struct ccm_softc *sc; + int err, rid; + phandle_t node; + int i; + + sc = device_get_softc(dev); + err = 0; + + /* Allocate bus_space resources. */ + rid = 0; + sc->clks = imx_clks; + sc->nclks = nitems(imx_clks); + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + err = ENXIO; + goto out; + } + + 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 IMX_CLK_UNDEFINED: + break; + case IMX_CLK_LINK: + clknode_link_register(sc->clkdom, + sc->clks[i].clk.link); + break; + case IMX_CLK_FIXED: + clknode_fixed_register(sc->clkdom, + sc->clks[i].clk.fixed); + break; + case IMX_CLK_MUX: + imx_clk_mux_register(sc->clkdom, sc->clks[i].clk.mux); + break; + case IMX_CLK_GATE: + imx_clk_gate_register(sc->clkdom, sc->clks[i].clk.gate); + break; + case IMX_CLK_COMPOSITE: + imx_clk_composite_register(sc->clkdom, sc->clks[i].clk.composite); + break; + case IMX_CLK_SSCG_PLL: + imx_clk_sscg_pll_register(sc->clkdom, sc->clks[i].clk.sscg_pll); + break; + case IMX_CLK_FRAC_PLL: + imx_clk_frac_pll_register(sc->clkdom, sc->clks[i].clk.frac_pll); + break; + case IMX_CLK_DIV: + clknode_div_register(sc->clkdom, sc->clks[i].clk.div); + break; + default: + device_printf(dev, "Unknown clock type %d\n", sc->clks[i].type); + return (ENXIO); + } + } + + if (clkdom_finit(sc->clkdom) != 0) + panic("cannot finalize clkdom initialization\n"); + + if (bootverbose) + clkdom_dump(sc->clkdom); + + node = ofw_bus_get_node(dev); + clk_set_assigned(dev, node); + + err = 0; + +out: + + if (err != 0) + ccm_detach(dev); + + return (err); +} + +static int +ccm_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_is_compatible(dev, "fsl,imx8mq-ccm") == 0) + return (ENXIO); + + device_set_desc(dev, "Freescale i.MX8 Clock Control Module"); + + return (BUS_PROBE_DEFAULT); +} + +static int +imx_ccm_write_4(device_t dev, bus_addr_t addr, uint32_t val) +{ + struct ccm_softc *sc; + + sc = device_get_softc(dev); + CCU_WRITE4(sc, addr, val); + return (0); +} + +static int +imx_ccm_read_4(device_t dev, bus_addr_t addr, uint32_t *val) +{ + struct ccm_softc *sc; + + sc = device_get_softc(dev); + + *val = CCU_READ4(sc, addr); + return (0); +} + +static int +imx_ccm_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) +{ + struct ccm_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 void +imx_ccm_device_lock(device_t dev) +{ + struct ccm_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->mtx); +} + +static void +imx_ccm_device_unlock(device_t dev) +{ + struct ccm_softc *sc; + + sc = device_get_softc(dev); + mtx_unlock(&sc->mtx); +} + +static device_method_t ccm_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ccm_probe), + DEVMETHOD(device_attach, ccm_attach), + DEVMETHOD(device_detach, ccm_detach), + + /* clkdev interface */ + DEVMETHOD(clkdev_write_4, imx_ccm_write_4), + DEVMETHOD(clkdev_read_4, imx_ccm_read_4), + DEVMETHOD(clkdev_modify_4, imx_ccm_modify_4), + DEVMETHOD(clkdev_device_lock, imx_ccm_device_lock), + DEVMETHOD(clkdev_device_unlock, imx_ccm_device_unlock), + + DEVMETHOD_END +}; + +static driver_t ccm_driver = { + "ccm", + ccm_methods, + sizeof(struct ccm_softc) +}; + +static devclass_t ccm_devclass; + +EARLY_DRIVER_MODULE(ccm, simplebus, ccm_driver, ccm_devclass, 0, 0, + BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); Index: head/sys/arm64/freescale/imx/imx_ccm_clk.h =================================================================== --- head/sys/arm64/freescale/imx/imx_ccm_clk.h +++ head/sys/arm64/freescale/imx/imx_ccm_clk.h @@ -0,0 +1,212 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Oleksandr Tymoshenko + * + * 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 IMX6_CCM_CLK_H +#define IMX6_CCM_CLK_H + +#include +#include +#include +#include +#include + +enum imx_clk_type { + IMX_CLK_UNDEFINED = 0, + IMX_CLK_FIXED, + IMX_CLK_LINK, + IMX_CLK_MUX, + IMX_CLK_GATE, + IMX_CLK_COMPOSITE, + IMX_CLK_SSCG_PLL, + IMX_CLK_FRAC_PLL, + IMX_CLK_DIV, +}; + +struct imx_clk { + enum imx_clk_type type; + union { + struct clk_fixed_def *fixed; + struct clk_link_def *link; + struct imx_clk_mux_def *mux; + struct imx_clk_gate_def *gate; + struct imx_clk_composite_def *composite; + struct imx_clk_sscg_pll_def *sscg_pll; + struct imx_clk_frac_pll_def *frac_pll; + struct clk_div_def *div; + } clk; +}; + +/* Linked clock. */ +#define LINK(_id, _name) \ +{ \ + .type = IMX_CLK_LINK, \ + .clk.link = &(struct clk_link_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = NULL, \ + .clkdef.parent_cnt = 0, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + }, \ +} + +/* Complex clock without divider (multiplexer only). */ +#define MUX(_id, _name, _pn, _f, _mo, _ms, _mw) \ +{ \ + .type = IMX_CLK_MUX, \ + .clk.mux = &(struct imx_clk_mux_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = _pn, \ + .clkdef.parent_cnt = nitems(_pn), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = _mo, \ + .shift = _ms, \ + .width = _mw, \ + .mux_flags = _f, \ + }, \ +} + +/* Fixed frequency clock */ +#define FIXED(_id, _name, _freq) \ +{ \ + .type = IMX_CLK_FIXED, \ + .clk.fixed = &(struct clk_fixed_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .freq = _freq, \ + }, \ +} + +/* Fixed factor multipier/divider. */ +#define FFACT(_id, _name, _pname, _mult, _div) \ +{ \ + .type = IMX_CLK_FIXED, \ + .clk.fixed = &(struct clk_fixed_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .mult = _mult, \ + .div = _div, \ + }, \ +} + +/* Clock gate */ +#define GATE(_id, _name, _pname, _o, _shift) \ +{ \ + .type = IMX_CLK_GATE, \ + .clk.gate = &(struct imx_clk_gate_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = _o, \ + .shift = _shift, \ + .mask = 1, \ + }, \ +} + +/* Root clock gate */ +#define ROOT_GATE(_id, _name, _pname, _reg) \ +{ \ + .type = IMX_CLK_GATE, \ + .clk.gate = &(struct imx_clk_gate_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = _reg, \ + .shift = 0, \ + .mask = 3, \ + }, \ +} + +/* Composite clock with GATE, MUX, PRE_DIV, and POST_DIV */ +#define COMPOSITE(_id, _name, _pn, _o, _flags) \ +{ \ + .type = IMX_CLK_COMPOSITE, \ + .clk.composite = &(struct imx_clk_composite_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = _pn, \ + .clkdef.parent_cnt = nitems(_pn), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = _o, \ + .flags = _flags, \ + }, \ +} + +/* SSCG PLL */ +#define SSCG_PLL(_id, _name, _pn, _o) \ +{ \ + .type = IMX_CLK_SSCG_PLL, \ + .clk.composite = &(struct imx_clk_composite_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = _pn, \ + .clkdef.parent_cnt = nitems(_pn), \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = _o, \ + }, \ +} + +/* Fractional PLL */ +#define FRAC_PLL(_id, _name, _pname, _o) \ +{ \ + .type = IMX_CLK_FRAC_PLL, \ + .clk.frac_pll = &(struct imx_clk_frac_pll_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = _o, \ + }, \ +} + +#define DIV(_id, _name, _pname, _o, _shift, _width) \ +{ \ + .type = IMX_CLK_DIV, \ + .clk.div = &(struct clk_div_def) { \ + .clkdef.id = _id, \ + .clkdef.name = _name, \ + .clkdef.parent_names = (const char *[]){_pname}, \ + .clkdef.parent_cnt = 1, \ + .clkdef.flags = CLK_NODE_STATIC_STRINGS, \ + .offset = _o, \ + .i_shift = _shift, \ + .i_width = _width, \ + }, \ +} + +#endif Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -3172,6 +3172,7 @@ dev/uart/uart_core.c optional uart dev/uart/uart_cpu_acpi.c optional uart acpi dev/uart/uart_dbg.c optional uart gdb +dev/uart/uart_dev_imx.c optional uart uart_imx fdt dev/uart/uart_dev_msm.c optional uart uart_msm fdt dev/uart/uart_dev_mvebu.c optional uart uart_mvebu dev/uart/uart_dev_ns8250.c optional uart uart_ns8250 | uart uart_snps Index: head/sys/conf/files.arm64 =================================================================== --- head/sys/conf/files.arm64 +++ head/sys/conf/files.arm64 @@ -395,3 +395,18 @@ arm64/rockchip/clk/rk3328_cru.c optional fdt soc_rockchip_rk3328 arm64/rockchip/clk/rk3399_cru.c optional fdt soc_rockchip_rk3399 arm64/rockchip/clk/rk3399_pmucru.c optional fdt soc_rockchip_rk3399 + +# i.MX8 Clock support +arm64/freescale/imx/imx8mq_ccm.c optional fdt soc_freescale_imx8 +arm64/freescale/imx/clk/imx_clk_gate.c optional fdt soc_freescale_imx8 +arm64/freescale/imx/clk/imx_clk_mux.c optional fdt soc_freescale_imx8 +arm64/freescale/imx/clk/imx_clk_composite.c optional fdt soc_freescale_imx8 +arm64/freescale/imx/clk/imx_clk_sscg_pll.c optional fdt soc_freescale_imx8 +arm64/freescale/imx/clk/imx_clk_frac_pll.c optional fdt soc_freescale_imx8 + +# iMX drivers +arm/freescale/imx/imx_gpio.c optional gpio +arm/freescale/imx/imx_i2c.c optional fsliic +arm/freescale/imx/imx_machdep.c standard +arm64/freescale/imx/imx7gpc.c optional fdt soc_freescale_imx8 +dev/ffec/if_ffec.c optional ffec Index: head/sys/conf/options.arm64 =================================================================== --- head/sys/conf/options.arm64 +++ head/sys/conf/options.arm64 @@ -22,6 +22,7 @@ SOC_BRCM_BCM2837 opt_soc.h SOC_BRCM_BCM2838 opt_soc.h SOC_CAVM_THUNDERX opt_soc.h +SOC_FREESCALE_IMX8 opt_soc.h SOC_HISI_HI6220 opt_soc.h SOC_INTEL_STRATIX10 opt_soc.h SOC_MARVELL_8K opt_soc.h Index: head/sys/dev/ffec/if_ffec.c =================================================================== --- head/sys/dev/ffec/if_ffec.c +++ head/sys/dev/ffec/if_ffec.c @@ -123,6 +123,7 @@ {"fsl,imx53-fec", FECTYPE_IMX53}, {"fsl,imx6q-fec", FECTYPE_IMX6 | FECFLAG_RACC | FECFLAG_GBE }, {"fsl,imx6ul-fec", FECTYPE_IMX6 | FECFLAG_RACC }, + {"fsl,imx6sx-fec", FECTYPE_IMX6 | FECFLAG_RACC }, {"fsl,imx7d-fec", FECTYPE_IMX6 | FECFLAG_RACC | FECFLAG_GBE | FECFLAG_AVB }, {"fsl,mvf600-fec", FECTYPE_MVF | FECFLAG_RACC }, Index: head/sys/dev/uart/uart_dev_imx.c =================================================================== --- head/sys/dev/uart/uart_dev_imx.c +++ head/sys/dev/uart/uart_dev_imx.c @@ -40,13 +40,21 @@ #include #include #include -#include #include #include #include #include #include + +#if defined(EXT_RESOURCES) && defined(__aarch64__) +#define IMX_ENABLE_CLOCKS +#endif + +#ifdef IMX_ENABLE_CLOCKS +#include +#endif + #include "uart_if.h" #include @@ -125,7 +133,7 @@ */ i = (GETREG(bas, REG(UFCR)) & IMXUART_UFCR_RFDIV_MASK) >> IMXUART_UFCR_RFDIV_SHIFT; - rate = imx_ccm_uart_hz() / predivs[i]; + rate = bas->rclk / predivs[i]; ubir = GETREG(bas, REG(UBIR)) + 1; ubmr = GETREG(bas, REG(UBMR)) + 1; baud = ((rate / 16 ) * ubir) / ubmr; @@ -193,8 +201,8 @@ * Note that a quirk of the hardware requires that both UBIR and UBMR be * set back to back in order for the change to take effect. */ - if (baudrate > 0) { - baseclk = imx_ccm_uart_hz(); + if ((baudrate > 0) && (bas->rclk != 0)) { + baseclk = bas->rclk; reg = GETREG(bas, REG(UFCR)); reg = (reg & ~IMXUART_UFCR_RFDIV_MASK) | IMXUART_UFCR_RFDIV_DIV1; SETREG(bas, REG(UFCR), reg); @@ -323,13 +331,58 @@ i = (i & s) ? (i & ~s) | d : i; \ } +#ifdef IMX_ENABLE_CLOCKS static int +imx_uart_setup_clocks(struct uart_softc *sc) +{ + struct uart_bas *bas; + clk_t ipgclk, perclk; + uint64_t freq; + int error; + + bas = &sc->sc_bas; + + if (clk_get_by_ofw_name(sc->sc_dev, 0, "ipg", &ipgclk) != 0) + return (ENOENT); + + if (clk_get_by_ofw_name(sc->sc_dev, 0, "per", &perclk) != 0) { + return (ENOENT); + } + + error = clk_enable(ipgclk); + if (error != 0) { + device_printf(sc->sc_dev, "cannot enable ipg clock\n"); + return (error); + } + + error = clk_get_freq(perclk, &freq); + if (error != 0) { + device_printf(sc->sc_dev, "cannot get frequency\n"); + return (error); + } + + bas->rclk = (uint32_t)freq; + + return (0); +} +#endif + +static int imx_uart_bus_attach(struct uart_softc *sc) { struct uart_bas *bas; struct uart_devinfo *di; bas = &sc->sc_bas; + +#ifdef IMX_ENABLE_CLOCKS + int error = imx_uart_setup_clocks(sc); + if (error) + return (error); +#else + bas->rclk = imx_ccm_uart_hz(); +#endif + if (sc->sc_sysdev != NULL) { di = sc->sc_sysdev; imx_uart_init(bas, di->baudrate, di->databits, di->stopbits, Index: head/sys/modules/dtb/imx8/Makefile =================================================================== --- head/sys/modules/dtb/imx8/Makefile +++ head/sys/modules/dtb/imx8/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ +# All the dts files for imx8 systems we support. +DTS= \ + freescale/imx8mq-evk.dts \ + freescale/imx8mq-nitrogen.dts + +.include