diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/clk/imx | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/clk/imx')
55 files changed, 17985 insertions, 0 deletions
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig new file mode 100644 index 000000000..f219004b8 --- /dev/null +++ b/drivers/clk/imx/Kconfig @@ -0,0 +1,122 @@ +# SPDX-License-Identifier: GPL-2.0 +# common clock support for NXP i.MX SoC family. +config MXC_CLK + tristate "IMX clock" + depends on ARCH_MXC || COMPILE_TEST + +config MXC_CLK_SCU + tristate + +config CLK_IMX1 + def_bool SOC_IMX1 + select MXC_CLK + +config CLK_IMX25 + def_bool SOC_IMX25 + select MXC_CLK + +config CLK_IMX27 + def_bool SOC_IMX27 + select MXC_CLK + +config CLK_IMX31 + def_bool SOC_IMX31 + select MXC_CLK + +config CLK_IMX35 + def_bool SOC_IMX35 + select MXC_CLK + +config CLK_IMX5 + def_bool SOC_IMX5 + select MXC_CLK + +config CLK_IMX6Q + def_bool SOC_IMX6Q + select MXC_CLK + +config CLK_IMX6SL + def_bool SOC_IMX6SL + select MXC_CLK + +config CLK_IMX6SLL + def_bool SOC_IMX6SLL + select MXC_CLK + +config CLK_IMX6SX + def_bool SOC_IMX6SX + select MXC_CLK + +config CLK_IMX6UL + def_bool SOC_IMX6UL + select MXC_CLK + +config CLK_IMX7D + def_bool SOC_IMX7D + select MXC_CLK + +config CLK_IMX7ULP + def_bool SOC_IMX7ULP + select MXC_CLK + +config CLK_VF610 + def_bool SOC_VF610 + select MXC_CLK + +config CLK_IMX8MM + tristate "IMX8MM CCM Clock Driver" + depends on ARCH_MXC || COMPILE_TEST + select MXC_CLK + help + Build the driver for i.MX8MM CCM Clock Driver + +config CLK_IMX8MN + tristate "IMX8MN CCM Clock Driver" + depends on ARCH_MXC || COMPILE_TEST + select MXC_CLK + help + Build the driver for i.MX8MN CCM Clock Driver + +config CLK_IMX8MP + tristate "IMX8MP CCM Clock Driver" + depends on ARCH_MXC || COMPILE_TEST + select MXC_CLK + help + Build the driver for i.MX8MP CCM Clock Driver + +config CLK_IMX8MQ + tristate "IMX8MQ CCM Clock Driver" + depends on ARCH_MXC || COMPILE_TEST + select MXC_CLK + help + Build the driver for i.MX8MQ CCM Clock Driver + +config CLK_IMX8QXP + tristate "IMX8QXP SCU Clock" + depends on (ARCH_MXC && ARM64) || COMPILE_TEST + depends on IMX_SCU && HAVE_ARM_SMCCC + select MXC_CLK_SCU + select MXC_CLK + help + Build the driver for IMX8QXP SCU based clocks. + +config CLK_IMX8ULP + tristate "IMX8ULP CCM Clock Driver" + depends on ARCH_MXC || COMPILE_TEST + select MXC_CLK + help + Build the driver for i.MX8ULP CCM Clock Driver + +config CLK_IMX93 + tristate "IMX93 CCM Clock Driver" + depends on ARCH_MXC || COMPILE_TEST + select MXC_CLK + help + Build the driver for i.MX93 CCM Clock Driver + +config CLK_IMXRT1050 + tristate "IMXRT1050 CCM Clock Driver" + depends on SOC_IMXRT + select MXC_CLK + help + Build the driver for i.MXRT1050 CCM Clock Driver diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile new file mode 100644 index 000000000..e8aacb0ee --- /dev/null +++ b/drivers/clk/imx/Makefile @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0 + +mxc-clk-objs += clk.o +mxc-clk-objs += clk-busy.o +mxc-clk-objs += clk-composite-7ulp.o +mxc-clk-objs += clk-composite-8m.o +mxc-clk-objs += clk-composite-93.o +mxc-clk-objs += clk-fracn-gppll.o +mxc-clk-objs += clk-cpu.o +mxc-clk-objs += clk-divider-gate.o +mxc-clk-objs += clk-fixup-div.o +mxc-clk-objs += clk-fixup-mux.o +mxc-clk-objs += clk-frac-pll.o +mxc-clk-objs += clk-gate2.o +mxc-clk-objs += clk-gate-93.o +mxc-clk-objs += clk-gate-exclusive.o +mxc-clk-objs += clk-pfd.o +mxc-clk-objs += clk-pfdv2.o +mxc-clk-objs += clk-pllv1.o +mxc-clk-objs += clk-pllv2.o +mxc-clk-objs += clk-pllv3.o +mxc-clk-objs += clk-pllv4.o +mxc-clk-objs += clk-pll14xx.o +mxc-clk-objs += clk-sscg-pll.o +obj-$(CONFIG_MXC_CLK) += mxc-clk.o + +obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o +obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o +obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o +obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o + +obj-$(CONFIG_CLK_IMX93) += clk-imx93.o + +obj-$(CONFIG_MXC_CLK_SCU) += clk-imx-scu.o clk-imx-lpcg-scu.o +clk-imx-scu-$(CONFIG_CLK_IMX8QXP) += clk-scu.o clk-imx8qxp.o \ + clk-imx8qxp-rsrc.o clk-imx8qm-rsrc.o \ + clk-imx8dxl-rsrc.o +clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o + +obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o + +obj-$(CONFIG_CLK_IMX1) += clk-imx1.o +obj-$(CONFIG_CLK_IMX25) += clk-imx25.o +obj-$(CONFIG_CLK_IMX27) += clk-imx27.o +obj-$(CONFIG_CLK_IMX31) += clk-imx31.o +obj-$(CONFIG_CLK_IMX35) += clk-imx35.o +obj-$(CONFIG_CLK_IMX5) += clk-imx5.o +obj-$(CONFIG_CLK_IMX6Q) += clk-imx6q.o +obj-$(CONFIG_CLK_IMX6SL) += clk-imx6sl.o +obj-$(CONFIG_CLK_IMX6SLL) += clk-imx6sll.o +obj-$(CONFIG_CLK_IMX6SX) += clk-imx6sx.o +obj-$(CONFIG_CLK_IMX6UL) += clk-imx6ul.o +obj-$(CONFIG_CLK_IMX7D) += clk-imx7d.o +obj-$(CONFIG_CLK_IMX7ULP) += clk-imx7ulp.o +obj-$(CONFIG_CLK_IMXRT1050) += clk-imxrt1050.o +obj-$(CONFIG_CLK_VF610) += clk-vf610.o diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c new file mode 100644 index 000000000..6f1731164 --- /dev/null +++ b/drivers/clk/imx/clk-busy.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/err.h> +#include "clk.h" + +static int clk_busy_wait(void __iomem *reg, u8 shift) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + while (readl_relaxed(reg) & (1 << shift)) + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + + return 0; +} + +struct clk_busy_divider { + struct clk_divider div; + const struct clk_ops *div_ops; + void __iomem *reg; + u8 shift; +}; + +static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw) +{ + struct clk_divider *div = to_clk_divider(hw); + + return container_of(div, struct clk_busy_divider, div); +} + +static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_busy_divider *busy = to_clk_busy_divider(hw); + + return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate); +} + +static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_busy_divider *busy = to_clk_busy_divider(hw); + + return busy->div_ops->round_rate(&busy->div.hw, rate, prate); +} + +static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_busy_divider *busy = to_clk_busy_divider(hw); + int ret; + + ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate); + if (!ret) + ret = clk_busy_wait(busy->reg, busy->shift); + + return ret; +} + +static const struct clk_ops clk_busy_divider_ops = { + .recalc_rate = clk_busy_divider_recalc_rate, + .round_rate = clk_busy_divider_round_rate, + .set_rate = clk_busy_divider_set_rate, +}; + +struct clk_hw *imx_clk_hw_busy_divider(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, + void __iomem *busy_reg, u8 busy_shift) +{ + struct clk_busy_divider *busy; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + busy = kzalloc(sizeof(*busy), GFP_KERNEL); + if (!busy) + return ERR_PTR(-ENOMEM); + + busy->reg = busy_reg; + busy->shift = busy_shift; + + busy->div.reg = reg; + busy->div.shift = shift; + busy->div.width = width; + busy->div.lock = &imx_ccm_lock; + busy->div_ops = &clk_divider_ops; + + init.name = name; + init.ops = &clk_busy_divider_ops; + init.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL; + init.parent_names = &parent_name; + init.num_parents = 1; + + busy->div.hw.init = &init; + + hw = &busy->div.hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(busy); + return ERR_PTR(ret); + } + + return hw; +} + +struct clk_busy_mux { + struct clk_mux mux; + const struct clk_ops *mux_ops; + void __iomem *reg; + u8 shift; +}; + +static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw) +{ + struct clk_mux *mux = to_clk_mux(hw); + + return container_of(mux, struct clk_busy_mux, mux); +} + +static u8 clk_busy_mux_get_parent(struct clk_hw *hw) +{ + struct clk_busy_mux *busy = to_clk_busy_mux(hw); + + return busy->mux_ops->get_parent(&busy->mux.hw); +} + +static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_busy_mux *busy = to_clk_busy_mux(hw); + int ret; + + ret = busy->mux_ops->set_parent(&busy->mux.hw, index); + if (!ret) + ret = clk_busy_wait(busy->reg, busy->shift); + + return ret; +} + +static const struct clk_ops clk_busy_mux_ops = { + .get_parent = clk_busy_mux_get_parent, + .set_parent = clk_busy_mux_set_parent, +}; + +struct clk_hw *imx_clk_hw_busy_mux(const char *name, void __iomem *reg, u8 shift, + u8 width, void __iomem *busy_reg, u8 busy_shift, + const char * const *parent_names, int num_parents) +{ + struct clk_busy_mux *busy; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + busy = kzalloc(sizeof(*busy), GFP_KERNEL); + if (!busy) + return ERR_PTR(-ENOMEM); + + busy->reg = busy_reg; + busy->shift = busy_shift; + + busy->mux.reg = reg; + busy->mux.shift = shift; + busy->mux.mask = BIT(width) - 1; + busy->mux.lock = &imx_ccm_lock; + busy->mux_ops = &clk_mux_ops; + + init.name = name; + init.ops = &clk_busy_mux_ops; + init.flags = CLK_IS_CRITICAL; + init.parent_names = parent_names; + init.num_parents = num_parents; + + busy->mux.hw.init = &init; + + hw = &busy->mux.hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(busy); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c new file mode 100644 index 000000000..4eedd45db --- /dev/null +++ b/drivers/clk/imx/clk-composite-7ulp.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + */ + +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "../clk-fractional-divider.h" +#include "clk.h" + +#define PCG_PCS_SHIFT 24 +#define PCG_PCS_MASK 0x7 +#define PCG_CGC_SHIFT 30 +#define PCG_FRAC_SHIFT 3 +#define PCG_FRAC_WIDTH 1 +#define PCG_FRAC_MASK BIT(3) +#define PCG_PCD_SHIFT 0 +#define PCG_PCD_WIDTH 3 +#define PCG_PCD_MASK 0x7 + +#define SW_RST BIT(28) + +static int pcc_gate_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + unsigned long flags; + u32 val; + int ret; + + ret = clk_gate_ops.enable(hw); + if (ret) + return ret; + + spin_lock_irqsave(gate->lock, flags); + /* + * release the sw reset for peripherals associated with + * with this pcc clock. + */ + val = readl(gate->reg); + val |= SW_RST; + writel(val, gate->reg); + + spin_unlock_irqrestore(gate->lock, flags); + + return 0; +} + +static void pcc_gate_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int pcc_gate_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops pcc_gate_ops = { + .enable = pcc_gate_enable, + .disable = pcc_gate_disable, + .is_enabled = pcc_gate_is_enabled, +}; + +static struct clk_hw *imx_ulp_clk_hw_composite(const char *name, + const char * const *parent_names, + int num_parents, bool mux_present, + bool rate_present, bool gate_present, + void __iomem *reg, bool has_swrst) +{ + struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL; + struct clk_fractional_divider *fd = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_hw *hw; + u32 val; + + if (mux_present) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + mux_hw = &mux->hw; + mux->reg = reg; + mux->shift = PCG_PCS_SHIFT; + mux->mask = PCG_PCS_MASK; + if (has_swrst) + mux->lock = &imx_ccm_lock; + } + + if (rate_present) { + fd = kzalloc(sizeof(*fd), GFP_KERNEL); + if (!fd) { + kfree(mux); + return ERR_PTR(-ENOMEM); + } + fd_hw = &fd->hw; + fd->reg = reg; + fd->mshift = PCG_FRAC_SHIFT; + fd->mwidth = PCG_FRAC_WIDTH; + fd->mmask = PCG_FRAC_MASK; + fd->nshift = PCG_PCD_SHIFT; + fd->nwidth = PCG_PCD_WIDTH; + fd->nmask = PCG_PCD_MASK; + fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED; + if (has_swrst) + fd->lock = &imx_ccm_lock; + } + + if (gate_present) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + kfree(mux); + kfree(fd); + return ERR_PTR(-ENOMEM); + } + gate_hw = &gate->hw; + gate->reg = reg; + gate->bit_idx = PCG_CGC_SHIFT; + if (has_swrst) + gate->lock = &imx_ccm_lock; + /* + * make sure clock is gated during clock tree initialization, + * the HW ONLY allow clock parent/rate changed with clock gated, + * during clock tree initialization, clocks could be enabled + * by bootloader, so the HW status will mismatch with clock tree + * prepare count, then clock core driver will allow parent/rate + * change since the prepare count is zero, but HW actually + * prevent the parent/rate change due to the clock is enabled. + */ + val = readl_relaxed(reg); + val &= ~(1 << PCG_CGC_SHIFT); + writel_relaxed(val, reg); + } + + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, &clk_mux_ops, fd_hw, + &clk_fractional_divider_ops, gate_hw, + has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE | + CLK_SET_PARENT_GATE | CLK_SET_RATE_NO_REPARENT); + if (IS_ERR(hw)) { + kfree(mux); + kfree(fd); + kfree(gate); + } + + return hw; +} + +struct clk_hw *imx7ulp_clk_hw_composite(const char *name, const char * const *parent_names, + int num_parents, bool mux_present, bool rate_present, + bool gate_present, void __iomem *reg) +{ + return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present, + gate_present, reg, false); +} + +struct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *parent_names, + int num_parents, bool mux_present, bool rate_present, + bool gate_present, void __iomem *reg, bool has_swrst) +{ + return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present, + gate_present, reg, has_swrst); +} +EXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite); diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c new file mode 100644 index 000000000..3e9a092e1 --- /dev/null +++ b/drivers/clk/imx/clk-composite-8m.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP + */ + +#include <linux/clk-provider.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "clk.h" + +#define PCG_PREDIV_SHIFT 16 +#define PCG_PREDIV_WIDTH 3 +#define PCG_PREDIV_MAX 8 + +#define PCG_DIV_SHIFT 0 +#define PCG_CORE_DIV_WIDTH 3 +#define PCG_DIV_WIDTH 6 +#define PCG_DIV_MAX 64 + +#define PCG_PCS_SHIFT 24 +#define PCG_PCS_MASK 0x7 + +#define PCG_CGC_SHIFT 28 + +static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + unsigned long prediv_rate; + unsigned int prediv_value; + unsigned int div_value; + + prediv_value = readl(divider->reg) >> divider->shift; + prediv_value &= clk_div_mask(divider->width); + + prediv_rate = divider_recalc_rate(hw, parent_rate, prediv_value, + NULL, divider->flags, + divider->width); + + div_value = readl(divider->reg) >> PCG_DIV_SHIFT; + div_value &= clk_div_mask(PCG_DIV_WIDTH); + + return divider_recalc_rate(hw, prediv_rate, div_value, NULL, + divider->flags, PCG_DIV_WIDTH); +} + +static int imx8m_clk_composite_compute_dividers(unsigned long rate, + unsigned long parent_rate, + int *prediv, int *postdiv) +{ + int div1, div2; + int error = INT_MAX; + int ret = -EINVAL; + + *prediv = 1; + *postdiv = 1; + + for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) { + for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) { + int new_error = ((parent_rate / div1) / div2) - rate; + + if (abs(new_error) < abs(error)) { + *prediv = div1; + *postdiv = div2; + error = new_error; + ret = 0; + } + } + } + return ret; +} + +static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + int prediv_value; + int div_value; + + imx8m_clk_composite_compute_dividers(rate, *prate, + &prediv_value, &div_value); + rate = DIV_ROUND_UP(*prate, prediv_value); + + return DIV_ROUND_UP(rate, div_value); + +} + +static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + unsigned long flags; + int prediv_value; + int div_value; + int ret; + u32 orig, val; + + ret = imx8m_clk_composite_compute_dividers(rate, parent_rate, + &prediv_value, &div_value); + if (ret) + return -EINVAL; + + spin_lock_irqsave(divider->lock, flags); + + orig = readl(divider->reg); + val = orig & ~((clk_div_mask(divider->width) << divider->shift) | + (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT)); + + val |= (u32)(prediv_value - 1) << divider->shift; + val |= (u32)(div_value - 1) << PCG_DIV_SHIFT; + + if (val != orig) + writel(val, divider->reg); + + spin_unlock_irqrestore(divider->lock, flags); + + return ret; +} + +static const struct clk_ops imx8m_clk_composite_divider_ops = { + .recalc_rate = imx8m_clk_composite_divider_recalc_rate, + .round_rate = imx8m_clk_composite_divider_round_rate, + .set_rate = imx8m_clk_composite_divider_set_rate, +}; + +static u8 imx8m_clk_composite_mux_get_parent(struct clk_hw *hw) +{ + return clk_mux_ops.get_parent(hw); +} + +static int imx8m_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_mux *mux = to_clk_mux(hw); + u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); + unsigned long flags = 0; + u32 reg; + + if (mux->lock) + spin_lock_irqsave(mux->lock, flags); + + reg = readl(mux->reg); + reg &= ~(mux->mask << mux->shift); + val = val << mux->shift; + reg |= val; + /* + * write twice to make sure non-target interface + * SEL_A/B point the same clk input. + */ + writel(reg, mux->reg); + writel(reg, mux->reg); + + if (mux->lock) + spin_unlock_irqrestore(mux->lock, flags); + + return 0; +} + +static int +imx8m_clk_composite_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_mux_ops.determine_rate(hw, req); +} + + +static const struct clk_ops imx8m_clk_composite_mux_ops = { + .get_parent = imx8m_clk_composite_mux_get_parent, + .set_parent = imx8m_clk_composite_mux_set_parent, + .determine_rate = imx8m_clk_composite_mux_determine_rate, +}; + +struct clk_hw *__imx8m_clk_hw_composite(const char *name, + const char * const *parent_names, + int num_parents, void __iomem *reg, + u32 composite_flags, + unsigned long flags) +{ + struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw; + struct clk_hw *div_hw, *gate_hw = NULL; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + const struct clk_ops *divider_ops; + const struct clk_ops *mux_ops; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto fail; + + mux_hw = &mux->hw; + mux->reg = reg; + mux->shift = PCG_PCS_SHIFT; + mux->mask = PCG_PCS_MASK; + mux->lock = &imx_ccm_lock; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto fail; + + div_hw = &div->hw; + div->reg = reg; + if (composite_flags & IMX_COMPOSITE_CORE) { + div->shift = PCG_DIV_SHIFT; + div->width = PCG_CORE_DIV_WIDTH; + divider_ops = &clk_divider_ops; + mux_ops = &imx8m_clk_composite_mux_ops; + } else if (composite_flags & IMX_COMPOSITE_BUS) { + div->shift = PCG_PREDIV_SHIFT; + div->width = PCG_PREDIV_WIDTH; + divider_ops = &imx8m_clk_composite_divider_ops; + mux_ops = &imx8m_clk_composite_mux_ops; + } else { + div->shift = PCG_PREDIV_SHIFT; + div->width = PCG_PREDIV_WIDTH; + divider_ops = &imx8m_clk_composite_divider_ops; + mux_ops = &clk_mux_ops; + if (!(composite_flags & IMX_COMPOSITE_FW_MANAGED)) + flags |= CLK_SET_PARENT_GATE; + } + + div->lock = &imx_ccm_lock; + div->flags = CLK_DIVIDER_ROUND_CLOSEST; + + /* skip registering the gate ops if M4 is enabled */ + if (!mcore_booted) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto fail; + + gate_hw = &gate->hw; + gate->reg = reg; + gate->bit_idx = PCG_CGC_SHIFT; + gate->lock = &imx_ccm_lock; + } + + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, mux_ops, div_hw, + divider_ops, gate_hw, &clk_gate_ops, flags); + if (IS_ERR(hw)) + goto fail; + + return hw; + +fail: + kfree(gate); + kfree(div); + kfree(mux); + return ERR_CAST(hw); +} +EXPORT_SYMBOL_GPL(__imx8m_clk_hw_composite); diff --git a/drivers/clk/imx/clk-composite-93.c b/drivers/clk/imx/clk-composite-93.c new file mode 100644 index 000000000..74a66b020 --- /dev/null +++ b/drivers/clk/imx/clk-composite-93.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <linux/clk-provider.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> + +#include "clk.h" + +#define TIMEOUT_US 500U + +#define CCM_DIV_SHIFT 0 +#define CCM_DIV_WIDTH 8 +#define CCM_MUX_SHIFT 8 +#define CCM_MUX_MASK 3 +#define CCM_OFF_SHIFT 24 +#define CCM_BUSY_SHIFT 28 + +#define STAT_OFFSET 0x4 +#define AUTHEN_OFFSET 0x30 +#define TZ_NS_SHIFT 9 +#define TZ_NS_MASK BIT(9) + +#define WHITE_LIST_SHIFT 16 + +static int imx93_clk_composite_wait_ready(struct clk_hw *hw, void __iomem *reg) +{ + int ret; + u32 val; + + ret = readl_poll_timeout_atomic(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)), + 0, TIMEOUT_US); + if (ret) + pr_err("Slice[%s] busy timeout\n", clk_hw_get_name(hw)); + + return ret; +} + +static void imx93_clk_composite_gate_endisable(struct clk_hw *hw, int enable) +{ + struct clk_gate *gate = to_clk_gate(hw); + unsigned long flags; + u32 reg; + + if (gate->lock) + spin_lock_irqsave(gate->lock, flags); + + reg = readl(gate->reg); + + if (enable) + reg &= ~BIT(gate->bit_idx); + else + reg |= BIT(gate->bit_idx); + + writel(reg, gate->reg); + + imx93_clk_composite_wait_ready(hw, gate->reg); + + if (gate->lock) + spin_unlock_irqrestore(gate->lock, flags); +} + +static int imx93_clk_composite_gate_enable(struct clk_hw *hw) +{ + imx93_clk_composite_gate_endisable(hw, 1); + + return 0; +} + +static void imx93_clk_composite_gate_disable(struct clk_hw *hw) +{ + imx93_clk_composite_gate_endisable(hw, 0); +} + +static const struct clk_ops imx93_clk_composite_gate_ops = { + .enable = imx93_clk_composite_gate_enable, + .disable = imx93_clk_composite_gate_disable, + .is_enabled = clk_gate_is_enabled, +}; + +static unsigned long +imx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long +imx93_clk_composite_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) +{ + return clk_divider_ops.round_rate(hw, rate, prate); +} + +static int +imx93_clk_composite_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + return clk_divider_ops.determine_rate(hw, req); +} + +static int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + int value; + unsigned long flags = 0; + u32 val; + int ret; + + value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags); + if (value < 0) + return value; + + if (divider->lock) + spin_lock_irqsave(divider->lock, flags); + + val = readl(divider->reg); + val &= ~(clk_div_mask(divider->width) << divider->shift); + val |= (u32)value << divider->shift; + writel(val, divider->reg); + + ret = imx93_clk_composite_wait_ready(hw, divider->reg); + + if (divider->lock) + spin_unlock_irqrestore(divider->lock, flags); + + return ret; +} + +static const struct clk_ops imx93_clk_composite_divider_ops = { + .recalc_rate = imx93_clk_composite_divider_recalc_rate, + .round_rate = imx93_clk_composite_divider_round_rate, + .determine_rate = imx93_clk_composite_divider_determine_rate, + .set_rate = imx93_clk_composite_divider_set_rate, +}; + +static u8 imx93_clk_composite_mux_get_parent(struct clk_hw *hw) +{ + return clk_mux_ops.get_parent(hw); +} + +static int imx93_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_mux *mux = to_clk_mux(hw); + u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); + unsigned long flags = 0; + u32 reg; + int ret; + + if (mux->lock) + spin_lock_irqsave(mux->lock, flags); + + reg = readl(mux->reg); + reg &= ~(mux->mask << mux->shift); + val = val << mux->shift; + reg |= val; + writel(reg, mux->reg); + + ret = imx93_clk_composite_wait_ready(hw, mux->reg); + + if (mux->lock) + spin_unlock_irqrestore(mux->lock, flags); + + return ret; +} + +static int +imx93_clk_composite_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + return clk_mux_ops.determine_rate(hw, req); +} + +static const struct clk_ops imx93_clk_composite_mux_ops = { + .get_parent = imx93_clk_composite_mux_get_parent, + .set_parent = imx93_clk_composite_mux_set_parent, + .determine_rate = imx93_clk_composite_mux_determine_rate, +}; + +struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *parent_names, + int num_parents, void __iomem *reg, u32 domain_id, + unsigned long flags) +{ + struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw; + struct clk_hw *div_hw, *gate_hw; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + bool clk_ro = false; + u32 authen; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + goto fail; + + mux_hw = &mux->hw; + mux->reg = reg; + mux->shift = CCM_MUX_SHIFT; + mux->mask = CCM_MUX_MASK; + mux->lock = &imx_ccm_lock; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + goto fail; + + div_hw = &div->hw; + div->reg = reg; + div->shift = CCM_DIV_SHIFT; + div->width = CCM_DIV_WIDTH; + div->lock = &imx_ccm_lock; + div->flags = CLK_DIVIDER_ROUND_CLOSEST; + + authen = readl(reg + AUTHEN_OFFSET); + if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id))) + clk_ro = true; + + if (clk_ro) { + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, &clk_mux_ro_ops, div_hw, + &clk_divider_ro_ops, NULL, NULL, flags); + } else { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + goto fail; + + gate_hw = &gate->hw; + gate->reg = reg; + gate->bit_idx = CCM_OFF_SHIFT; + gate->lock = &imx_ccm_lock; + gate->flags = CLK_GATE_SET_TO_DISABLE; + + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, &imx93_clk_composite_mux_ops, div_hw, + &imx93_clk_composite_divider_ops, gate_hw, + &imx93_clk_composite_gate_ops, + flags | CLK_SET_RATE_NO_REPARENT); + } + + if (IS_ERR(hw)) + goto fail; + + return hw; + +fail: + kfree(gate); + kfree(div); + kfree(mux); + return ERR_CAST(hw); +} +EXPORT_SYMBOL_GPL(imx93_clk_composite_flags); diff --git a/drivers/clk/imx/clk-cpu.c b/drivers/clk/imx/clk-cpu.c new file mode 100644 index 000000000..cb6ca4cf0 --- /dev/null +++ b/drivers/clk/imx/clk-cpu.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 Lucas Stach <l.stach@pengutronix.de>, Pengutronix + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/export.h> +#include <linux/slab.h> +#include "clk.h" + +struct clk_cpu { + struct clk_hw hw; + struct clk *div; + struct clk *mux; + struct clk *pll; + struct clk *step; +}; + +static inline struct clk_cpu *to_clk_cpu(struct clk_hw *hw) +{ + return container_of(hw, struct clk_cpu, hw); +} + +static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_cpu *cpu = to_clk_cpu(hw); + + return clk_get_rate(cpu->div); +} + +static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_cpu *cpu = to_clk_cpu(hw); + + return clk_round_rate(cpu->pll, rate); +} + +static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cpu *cpu = to_clk_cpu(hw); + int ret; + + /* switch to PLL bypass clock */ + ret = clk_set_parent(cpu->mux, cpu->step); + if (ret) + return ret; + + /* reprogram PLL */ + ret = clk_set_rate(cpu->pll, rate); + if (ret) { + clk_set_parent(cpu->mux, cpu->pll); + return ret; + } + /* switch back to PLL clock */ + clk_set_parent(cpu->mux, cpu->pll); + + /* Ensure the divider is what we expect */ + clk_set_rate(cpu->div, rate); + + return 0; +} + +static const struct clk_ops clk_cpu_ops = { + .recalc_rate = clk_cpu_recalc_rate, + .round_rate = clk_cpu_round_rate, + .set_rate = clk_cpu_set_rate, +}; + +struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name, + struct clk *div, struct clk *mux, struct clk *pll, + struct clk *step) +{ + struct clk_cpu *cpu; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + cpu = kzalloc(sizeof(*cpu), GFP_KERNEL); + if (!cpu) + return ERR_PTR(-ENOMEM); + + cpu->div = div; + cpu->mux = mux; + cpu->pll = pll; + cpu->step = step; + + init.name = name; + init.ops = &clk_cpu_ops; + init.flags = CLK_IS_CRITICAL; + init.parent_names = &parent_name; + init.num_parents = 1; + + cpu->hw.init = &init; + hw = &cpu->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(cpu); + return ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx_clk_hw_cpu); diff --git a/drivers/clk/imx/clk-divider-gate.c b/drivers/clk/imx/clk-divider-gate.c new file mode 100644 index 000000000..26b210cba --- /dev/null +++ b/drivers/clk/imx/clk-divider-gate.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP. + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "clk.h" + +struct clk_divider_gate { + struct clk_divider divider; + u32 cached_val; +}; + +static inline struct clk_divider_gate *to_clk_divider_gate(struct clk_hw *hw) +{ + struct clk_divider *div = to_clk_divider(hw); + + return container_of(div, struct clk_divider_gate, divider); +} + +static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *div = to_clk_divider(hw); + unsigned int val; + + val = readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + if (!val) + return 0; + + return divider_recalc_rate(hw, parent_rate, val, div->table, + div->flags, div->width); +} + +static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(div->lock, flags); + + if (!clk_hw_is_enabled(hw)) { + val = div_gate->cached_val; + } else { + val = readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + } + + spin_unlock_irqrestore(div->lock, flags); + + if (!val) + return 0; + + return divider_recalc_rate(hw, parent_rate, val, div->table, + div->flags, div->width); +} + +static int clk_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_divider_ops.determine_rate(hw, req); +} + +static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags; + int value; + u32 val; + + value = divider_get_val(rate, parent_rate, div->table, + div->width, div->flags); + if (value < 0) + return value; + + spin_lock_irqsave(div->lock, flags); + + if (clk_hw_is_enabled(hw)) { + val = readl(div->reg); + val &= ~(clk_div_mask(div->width) << div->shift); + val |= (u32)value << div->shift; + writel(val, div->reg); + } else { + div_gate->cached_val = value; + } + + spin_unlock_irqrestore(div->lock, flags); + + return 0; +} + +static int clk_divider_enable(struct clk_hw *hw) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags; + u32 val; + + if (!div_gate->cached_val) { + pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw)); + return -EINVAL; + } + + spin_lock_irqsave(div->lock, flags); + /* restore div val */ + val = readl(div->reg); + val |= div_gate->cached_val << div->shift; + writel(val, div->reg); + + spin_unlock_irqrestore(div->lock, flags); + + return 0; +} + +static void clk_divider_disable(struct clk_hw *hw) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags; + u32 val; + + spin_lock_irqsave(div->lock, flags); + + /* store the current div val */ + val = readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + div_gate->cached_val = val; + writel(0, div->reg); + + spin_unlock_irqrestore(div->lock, flags); +} + +static int clk_divider_is_enabled(struct clk_hw *hw) +{ + struct clk_divider *div = to_clk_divider(hw); + u32 val; + + val = readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + + return val ? 1 : 0; +} + +static const struct clk_ops clk_divider_gate_ro_ops = { + .recalc_rate = clk_divider_gate_recalc_rate_ro, + .determine_rate = clk_divider_determine_rate, +}; + +static const struct clk_ops clk_divider_gate_ops = { + .recalc_rate = clk_divider_gate_recalc_rate, + .determine_rate = clk_divider_determine_rate, + .set_rate = clk_divider_gate_set_rate, + .enable = clk_divider_enable, + .disable = clk_divider_disable, + .is_enabled = clk_divider_is_enabled, +}; + +/* + * NOTE: In order to reuse the most code from the common divider, + * we also design our divider following the way that provids an extra + * clk_divider_flags, however it's fixed to CLK_DIVIDER_ONE_BASED by + * default as our HW is. Besides that it supports only CLK_DIVIDER_READ_ONLY + * flag which can be specified by user flexibly. + */ +struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u8 clk_divider_flags, + const struct clk_div_table *table, + spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_divider_gate *div_gate; + struct clk_hw *hw; + u32 val; + int ret; + + div_gate = kzalloc(sizeof(*div_gate), GFP_KERNEL); + if (!div_gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) + init.ops = &clk_divider_gate_ro_ops; + else + init.ops = &clk_divider_gate_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + div_gate->divider.reg = reg; + div_gate->divider.shift = shift; + div_gate->divider.width = width; + div_gate->divider.lock = lock; + div_gate->divider.table = table; + div_gate->divider.hw.init = &init; + div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags; + /* cache gate status */ + val = readl(reg) >> shift; + val &= clk_div_mask(width); + div_gate->cached_val = val; + + hw = &div_gate->divider.hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(div_gate); + hw = ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-fixup-div.c b/drivers/clk/imx/clk-fixup-div.c new file mode 100644 index 000000000..100ca828b --- /dev/null +++ b/drivers/clk/imx/clk-fixup-div.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +#define div_mask(d) ((1 << (d->width)) - 1) + +/** + * struct clk_fixup_div - imx integer fixup divider clock + * @divider: the parent class + * @ops: pointer to clk_ops of parent class + * @fixup: a hook to fixup the write value + * + * The imx fixup divider clock is a subclass of basic clk_divider + * with an addtional fixup hook. + */ +struct clk_fixup_div { + struct clk_divider divider; + const struct clk_ops *ops; + void (*fixup)(u32 *val); +}; + +static inline struct clk_fixup_div *to_clk_fixup_div(struct clk_hw *hw) +{ + struct clk_divider *divider = to_clk_divider(hw); + + return container_of(divider, struct clk_fixup_div, divider); +} + +static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); + + return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate); +} + +static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); + + return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate); +} + +static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned int divider, value; + unsigned long flags; + u32 val; + + divider = parent_rate / rate; + + /* Zero based divider */ + value = divider - 1; + + if (value > div_mask(div)) + value = div_mask(div); + + spin_lock_irqsave(div->lock, flags); + + val = readl(div->reg); + val &= ~(div_mask(div) << div->shift); + val |= value << div->shift; + fixup_div->fixup(&val); + writel(val, div->reg); + + spin_unlock_irqrestore(div->lock, flags); + + return 0; +} + +static const struct clk_ops clk_fixup_div_ops = { + .recalc_rate = clk_fixup_div_recalc_rate, + .round_rate = clk_fixup_div_round_rate, + .set_rate = clk_fixup_div_set_rate, +}; + +struct clk_hw *imx_clk_hw_fixup_divider(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width, + void (*fixup)(u32 *val)) +{ + struct clk_fixup_div *fixup_div; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (!fixup) + return ERR_PTR(-EINVAL); + + fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL); + if (!fixup_div) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_fixup_div_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent ? &parent : NULL; + init.num_parents = parent ? 1 : 0; + + fixup_div->divider.reg = reg; + fixup_div->divider.shift = shift; + fixup_div->divider.width = width; + fixup_div->divider.lock = &imx_ccm_lock; + fixup_div->divider.hw.init = &init; + fixup_div->ops = &clk_divider_ops; + fixup_div->fixup = fixup; + + hw = &fixup_div->divider.hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(fixup_div); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-fixup-mux.c b/drivers/clk/imx/clk-fixup-mux.c new file mode 100644 index 000000000..c82401570 --- /dev/null +++ b/drivers/clk/imx/clk-fixup-mux.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + */ + +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_fixup_mux - imx integer fixup multiplexer clock + * @mux: the parent class + * @ops: pointer to clk_ops of parent class + * @fixup: a hook to fixup the write value + * + * The imx fixup multiplexer clock is a subclass of basic clk_mux + * with an addtional fixup hook. + */ +struct clk_fixup_mux { + struct clk_mux mux; + const struct clk_ops *ops; + void (*fixup)(u32 *val); +}; + +static inline struct clk_fixup_mux *to_clk_fixup_mux(struct clk_hw *hw) +{ + struct clk_mux *mux = to_clk_mux(hw); + + return container_of(mux, struct clk_fixup_mux, mux); +} + +static u8 clk_fixup_mux_get_parent(struct clk_hw *hw) +{ + struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); + + return fixup_mux->ops->get_parent(&fixup_mux->mux.hw); +} + +static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); + struct clk_mux *mux = to_clk_mux(hw); + unsigned long flags; + u32 val; + + spin_lock_irqsave(mux->lock, flags); + + val = readl(mux->reg); + val &= ~(mux->mask << mux->shift); + val |= index << mux->shift; + fixup_mux->fixup(&val); + writel(val, mux->reg); + + spin_unlock_irqrestore(mux->lock, flags); + + return 0; +} + +static const struct clk_ops clk_fixup_mux_ops = { + .get_parent = clk_fixup_mux_get_parent, + .set_parent = clk_fixup_mux_set_parent, +}; + +struct clk_hw *imx_clk_hw_fixup_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char * const *parents, + int num_parents, void (*fixup)(u32 *val)) +{ + struct clk_fixup_mux *fixup_mux; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (!fixup) + return ERR_PTR(-EINVAL); + + fixup_mux = kzalloc(sizeof(*fixup_mux), GFP_KERNEL); + if (!fixup_mux) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_fixup_mux_ops; + init.parent_names = parents; + init.num_parents = num_parents; + init.flags = 0; + + fixup_mux->mux.reg = reg; + fixup_mux->mux.shift = shift; + fixup_mux->mux.mask = BIT(width) - 1; + fixup_mux->mux.lock = &imx_ccm_lock; + fixup_mux->mux.hw.init = &init; + fixup_mux->ops = &clk_mux_ops; + fixup_mux->fixup = fixup; + + hw = &fixup_mux->mux.hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(fixup_mux); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-frac-pll.c b/drivers/clk/imx/clk-frac-pll.c new file mode 100644 index 000000000..c703056fa --- /dev/null +++ b/drivers/clk/imx/clk-frac-pll.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP. + * + * This driver supports the fractional plls found in the imx8m SOCs + * + * Documentation for this fractional pll can be found at: + * https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834 + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> +#include <linux/bitfield.h> + +#include "clk.h" + +#define PLL_CFG0 0x0 +#define PLL_CFG1 0x4 + +#define PLL_LOCK_STATUS BIT(31) +#define PLL_PD_MASK BIT(19) +#define PLL_BYPASS_MASK BIT(14) +#define PLL_NEWDIV_VAL BIT(12) +#define PLL_NEWDIV_ACK BIT(11) +#define PLL_FRAC_DIV_MASK GENMASK(30, 7) +#define PLL_INT_DIV_MASK GENMASK(6, 0) +#define PLL_OUTPUT_DIV_MASK GENMASK(4, 0) +#define PLL_FRAC_DENOM 0x1000000 + +#define PLL_FRAC_LOCK_TIMEOUT 10000 +#define PLL_FRAC_ACK_TIMEOUT 500000 + +struct clk_frac_pll { + struct clk_hw hw; + void __iomem *base; +}; + +#define to_clk_frac_pll(_hw) container_of(_hw, struct clk_frac_pll, hw) + +static int clk_wait_lock(struct clk_frac_pll *pll) +{ + u32 val; + + return readl_poll_timeout(pll->base, val, val & PLL_LOCK_STATUS, 0, + PLL_FRAC_LOCK_TIMEOUT); +} + +static int clk_wait_ack(struct clk_frac_pll *pll) +{ + u32 val; + + /* return directly if the pll is in powerdown or in bypass */ + if (readl_relaxed(pll->base) & (PLL_PD_MASK | PLL_BYPASS_MASK)) + return 0; + + /* Wait for the pll's divfi and divff to be reloaded */ + return readl_poll_timeout(pll->base, val, val & PLL_NEWDIV_ACK, 0, + PLL_FRAC_ACK_TIMEOUT); +} + +static int clk_pll_prepare(struct clk_hw *hw) +{ + struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val; + + val = readl_relaxed(pll->base + PLL_CFG0); + val &= ~PLL_PD_MASK; + writel_relaxed(val, pll->base + PLL_CFG0); + + return clk_wait_lock(pll); +} + +static void clk_pll_unprepare(struct clk_hw *hw) +{ + struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val; + + val = readl_relaxed(pll->base + PLL_CFG0); + val |= PLL_PD_MASK; + writel_relaxed(val, pll->base + PLL_CFG0); +} + +static int clk_pll_is_prepared(struct clk_hw *hw) +{ + struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val; + + val = readl_relaxed(pll->base + PLL_CFG0); + return (val & PLL_PD_MASK) ? 0 : 1; +} + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val, divff, divfi, divq; + u64 temp64 = parent_rate; + u64 rate; + + val = readl_relaxed(pll->base + PLL_CFG0); + divq = (FIELD_GET(PLL_OUTPUT_DIV_MASK, val) + 1) * 2; + val = readl_relaxed(pll->base + PLL_CFG1); + divff = FIELD_GET(PLL_FRAC_DIV_MASK, val); + divfi = FIELD_GET(PLL_INT_DIV_MASK, val); + + temp64 *= 8; + temp64 *= divff; + do_div(temp64, PLL_FRAC_DENOM); + do_div(temp64, divq); + + rate = parent_rate * 8 * (divfi + 1); + do_div(rate, divq); + rate += temp64; + + return rate; +} + +static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + u64 parent_rate = *prate; + u32 divff, divfi; + u64 temp64; + + parent_rate *= 8; + rate *= 2; + temp64 = rate; + do_div(temp64, parent_rate); + divfi = temp64; + temp64 = rate - divfi * parent_rate; + temp64 *= PLL_FRAC_DENOM; + do_div(temp64, parent_rate); + divff = temp64; + + temp64 = parent_rate; + temp64 *= divff; + do_div(temp64, PLL_FRAC_DENOM); + + rate = parent_rate * divfi + temp64; + + return rate / 2; +} + +/* + * To simplify the clock calculation, we can keep the 'PLL_OUTPUT_VAL' at zero + * (means the PLL output will be divided by 2). So the PLL output can use + * the below formula: + * pllout = parent_rate * 8 / 2 * DIVF_VAL; + * where DIVF_VAL = 1 + DIVFI + DIVFF / 2^24. + */ +static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_frac_pll *pll = to_clk_frac_pll(hw); + u32 val, divfi, divff; + u64 temp64; + int ret; + + parent_rate *= 8; + rate *= 2; + divfi = rate / parent_rate; + temp64 = parent_rate * divfi; + temp64 = rate - temp64; + temp64 *= PLL_FRAC_DENOM; + do_div(temp64, parent_rate); + divff = temp64; + + val = readl_relaxed(pll->base + PLL_CFG1); + val &= ~(PLL_FRAC_DIV_MASK | PLL_INT_DIV_MASK); + val |= (divff << 7) | (divfi - 1); + writel_relaxed(val, pll->base + PLL_CFG1); + + val = readl_relaxed(pll->base + PLL_CFG0); + val &= ~0x1f; + writel_relaxed(val, pll->base + PLL_CFG0); + + /* Set the NEV_DIV_VAL to reload the DIVFI and DIVFF */ + val = readl_relaxed(pll->base + PLL_CFG0); + val |= PLL_NEWDIV_VAL; + writel_relaxed(val, pll->base + PLL_CFG0); + + ret = clk_wait_ack(pll); + + /* clear the NEV_DIV_VAL */ + val = readl_relaxed(pll->base + PLL_CFG0); + val &= ~PLL_NEWDIV_VAL; + writel_relaxed(val, pll->base + PLL_CFG0); + + return ret; +} + +static const struct clk_ops clk_frac_pll_ops = { + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .is_prepared = clk_pll_is_prepared, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +struct clk_hw *imx_clk_hw_frac_pll(const char *name, + const char *parent_name, + void __iomem *base) +{ + struct clk_init_data init; + struct clk_frac_pll *pll; + struct clk_hw *hw; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_frac_pll_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + pll->base = base; + pll->hw.init = &init; + + hw = &pll->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx_clk_hw_frac_pll); diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c new file mode 100644 index 000000000..f6674110a --- /dev/null +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 NXP + */ + +#include <linux/bitfield.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> +#include <asm/div64.h> + +#include "clk.h" + +#define PLL_CTRL 0x0 +#define HW_CTRL_SEL BIT(16) +#define CLKMUX_BYPASS BIT(2) +#define CLKMUX_EN BIT(1) +#define POWERUP_MASK BIT(0) + +#define PLL_ANA_PRG 0x10 +#define PLL_SPREAD_SPECTRUM 0x30 + +#define PLL_NUMERATOR 0x40 +#define PLL_MFN_MASK GENMASK(31, 2) + +#define PLL_DENOMINATOR 0x50 +#define PLL_MFD_MASK GENMASK(29, 0) + +#define PLL_DIV 0x60 +#define PLL_MFI_MASK GENMASK(24, 16) +#define PLL_RDIV_MASK GENMASK(15, 13) +#define PLL_ODIV_MASK GENMASK(7, 0) + +#define PLL_DFS_CTRL(x) (0x70 + (x) * 0x10) + +#define PLL_STATUS 0xF0 +#define LOCK_STATUS BIT(0) + +#define DFS_STATUS 0xF4 + +#define LOCK_TIMEOUT_US 200 + +#define PLL_FRACN_GP(_rate, _mfi, _mfn, _mfd, _rdiv, _odiv) \ + { \ + .rate = (_rate), \ + .mfi = (_mfi), \ + .mfn = (_mfn), \ + .mfd = (_mfd), \ + .rdiv = (_rdiv), \ + .odiv = (_odiv), \ + } + +struct clk_fracn_gppll { + struct clk_hw hw; + void __iomem *base; + const struct imx_fracn_gppll_rate_table *rate_table; + int rate_count; +}; + +/* + * Fvco = (Fref / rdiv) * (MFI + MFN / MFD) + * Fout = Fvco / odiv + * The (Fref / rdiv) should be in range 20MHz to 40MHz + * The Fvco should be in range 2.5Ghz to 5Ghz + */ +static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { + PLL_FRACN_GP(650000000U, 162, 50, 100, 0, 6), + PLL_FRACN_GP(594000000U, 198, 0, 1, 0, 8), + PLL_FRACN_GP(560000000U, 140, 0, 1, 0, 6), + PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8), + PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), + PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), + PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12), + PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10) +}; + +struct imx_fracn_gppll_clk imx_fracn_gppll = { + .rate_table = fracn_tbl, + .rate_count = ARRAY_SIZE(fracn_tbl), +}; +EXPORT_SYMBOL_GPL(imx_fracn_gppll); + +static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw) +{ + return container_of(hw, struct clk_fracn_gppll, hw); +} + +static const struct imx_fracn_gppll_rate_table * +imx_get_pll_settings(struct clk_fracn_gppll *pll, unsigned long rate) +{ + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) + if (rate == rate_table[i].rate) + return &rate_table[i]; + + return NULL; +} + +static long clk_fracn_gppll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + int i; + + /* Assuming rate_table is in descending order */ + for (i = 0; i < pll->rate_count; i++) + if (rate >= rate_table[i].rate) + return rate_table[i].rate; + + /* return minimum supported value */ + return rate_table[pll->rate_count - 1].rate; +} + +static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); + const struct imx_fracn_gppll_rate_table *rate_table = pll->rate_table; + u32 pll_numerator, pll_denominator, pll_div; + u32 mfi, mfn, mfd, rdiv, odiv; + u64 fvco = parent_rate; + long rate = 0; + int i; + + pll_numerator = readl_relaxed(pll->base + PLL_NUMERATOR); + mfn = FIELD_GET(PLL_MFN_MASK, pll_numerator); + + pll_denominator = readl_relaxed(pll->base + PLL_DENOMINATOR); + mfd = FIELD_GET(PLL_MFD_MASK, pll_denominator); + + pll_div = readl_relaxed(pll->base + PLL_DIV); + mfi = FIELD_GET(PLL_MFI_MASK, pll_div); + + rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); + odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); + + /* + * Sometimes, the recalculated rate has deviation due to + * the frac part. So find the accurate pll rate from the table + * first, if no match rate in the table, use the rate calculated + * from the equation below. + */ + for (i = 0; i < pll->rate_count; i++) { + if (rate_table[i].mfn == mfn && rate_table[i].mfi == mfi && + rate_table[i].mfd == mfd && rate_table[i].rdiv == rdiv && + rate_table[i].odiv == odiv) + rate = rate_table[i].rate; + } + + if (rate) + return (unsigned long)rate; + + if (!rdiv) + rdiv = rdiv + 1; + + switch (odiv) { + case 0: + odiv = 2; + break; + case 1: + odiv = 3; + break; + default: + break; + } + + /* Fvco = Fref * (MFI + MFN / MFD) */ + fvco = fvco * mfi * mfd + fvco * mfn; + do_div(fvco, mfd * rdiv * odiv); + + return (unsigned long)fvco; +} + +static int clk_fracn_gppll_wait_lock(struct clk_fracn_gppll *pll) +{ + u32 val; + + return readl_poll_timeout(pll->base + PLL_STATUS, val, + val & LOCK_STATUS, 0, LOCK_TIMEOUT_US); +} + +static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); + const struct imx_fracn_gppll_rate_table *rate; + u32 tmp, pll_div, ana_mfn; + int ret; + + rate = imx_get_pll_settings(pll, drate); + + /* Hardware control select disable. PLL is control by register */ + tmp = readl_relaxed(pll->base + PLL_CTRL); + tmp &= ~HW_CTRL_SEL; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Disable output */ + tmp = readl_relaxed(pll->base + PLL_CTRL); + tmp &= ~CLKMUX_EN; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Power Down */ + tmp &= ~POWERUP_MASK; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Disable BYPASS */ + tmp &= ~CLKMUX_BYPASS; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv | + FIELD_PREP(PLL_MFI_MASK, rate->mfi); + writel_relaxed(pll_div, pll->base + PLL_DIV); + writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR); + writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR); + + /* Wait for 5us according to fracn mode pll doc */ + udelay(5); + + /* Enable Powerup */ + tmp |= POWERUP_MASK; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + /* Wait Lock */ + ret = clk_fracn_gppll_wait_lock(pll); + if (ret) + return ret; + + /* Enable output */ + tmp |= CLKMUX_EN; + writel_relaxed(tmp, pll->base + PLL_CTRL); + + ana_mfn = readl_relaxed(pll->base + PLL_STATUS); + ana_mfn = FIELD_GET(PLL_MFN_MASK, ana_mfn); + + WARN(ana_mfn != rate->mfn, "ana_mfn != rate->mfn\n"); + + return 0; +} + +static int clk_fracn_gppll_prepare(struct clk_hw *hw) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); + u32 val; + int ret; + + val = readl_relaxed(pll->base + PLL_CTRL); + if (val & POWERUP_MASK) + return 0; + + val |= CLKMUX_BYPASS; + writel_relaxed(val, pll->base + PLL_CTRL); + + val |= POWERUP_MASK; + writel_relaxed(val, pll->base + PLL_CTRL); + + val |= CLKMUX_EN; + writel_relaxed(val, pll->base + PLL_CTRL); + + ret = clk_fracn_gppll_wait_lock(pll); + if (ret) + return ret; + + val &= ~CLKMUX_BYPASS; + writel_relaxed(val, pll->base + PLL_CTRL); + + return 0; +} + +static int clk_fracn_gppll_is_prepared(struct clk_hw *hw) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); + u32 val; + + val = readl_relaxed(pll->base + PLL_CTRL); + + return (val & POWERUP_MASK) ? 1 : 0; +} + +static void clk_fracn_gppll_unprepare(struct clk_hw *hw) +{ + struct clk_fracn_gppll *pll = to_clk_fracn_gppll(hw); + u32 val; + + val = readl_relaxed(pll->base + PLL_CTRL); + val &= ~POWERUP_MASK; + writel_relaxed(val, pll->base + PLL_CTRL); +} + +static const struct clk_ops clk_fracn_gppll_ops = { + .prepare = clk_fracn_gppll_prepare, + .unprepare = clk_fracn_gppll_unprepare, + .is_prepared = clk_fracn_gppll_is_prepared, + .recalc_rate = clk_fracn_gppll_recalc_rate, + .round_rate = clk_fracn_gppll_round_rate, + .set_rate = clk_fracn_gppll_set_rate, +}; + +struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk) +{ + struct clk_fracn_gppll *pll; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = pll_clk->flags; + init.parent_names = &parent_name; + init.num_parents = 1; + init.ops = &clk_fracn_gppll_ops; + + pll->base = base; + pll->hw.init = &init; + pll->rate_table = pll_clk->rate_table; + pll->rate_count = pll_clk->rate_count; + + hw = &pll->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + pr_err("%s: failed to register pll %s %d\n", __func__, name, ret); + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll); diff --git a/drivers/clk/imx/clk-gate-93.c b/drivers/clk/imx/clk-gate-93.c new file mode 100644 index 000000000..ceb56b290 --- /dev/null +++ b/drivers/clk/imx/clk-gate-93.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022 NXP + * + * Peng Fan <peng.fan@nxp.com> + */ + +#include <linux/clk-provider.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> + +#include "clk.h" + +#define DIRECT_OFFSET 0x0 + +/* + * 0b000 - LPCG will be OFF in any CPU mode. + * 0b100 - LPCG will be ON in any CPU mode. + */ +#define LPM_SETTING_OFF 0x0 +#define LPM_SETTING_ON 0x4 + +#define LPM_CUR_OFFSET 0x1c + +#define AUTHEN_OFFSET 0x30 +#define CPULPM_EN BIT(2) +#define TZ_NS_SHIFT 9 +#define TZ_NS_MASK BIT(9) + +#define WHITE_LIST_SHIFT 16 + +struct imx93_clk_gate { + struct clk_hw hw; + void __iomem *reg; + u32 bit_idx; + u32 val; + u32 mask; + spinlock_t *lock; + unsigned int *share_count; +}; + +#define to_imx93_clk_gate(_hw) container_of(_hw, struct imx93_clk_gate, hw) + +static void imx93_clk_gate_do_hardware(struct clk_hw *hw, bool enable) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); + u32 val; + + val = readl(gate->reg + AUTHEN_OFFSET); + if (val & CPULPM_EN) { + val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF; + writel(val, gate->reg + LPM_CUR_OFFSET); + } else { + val = readl(gate->reg + DIRECT_OFFSET); + val &= ~(gate->mask << gate->bit_idx); + if (enable) + val |= (gate->val & gate->mask) << gate->bit_idx; + writel(val, gate->reg + DIRECT_OFFSET); + } +} + +static int imx93_clk_gate_enable(struct clk_hw *hw) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); + unsigned long flags; + + spin_lock_irqsave(gate->lock, flags); + + if (gate->share_count && (*gate->share_count)++ > 0) + goto out; + + imx93_clk_gate_do_hardware(hw, true); +out: + spin_unlock_irqrestore(gate->lock, flags); + + return 0; +} + +static void imx93_clk_gate_disable(struct clk_hw *hw) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); + unsigned long flags; + + spin_lock_irqsave(gate->lock, flags); + + if (gate->share_count) { + if (WARN_ON(*gate->share_count == 0)) + goto out; + else if (--(*gate->share_count) > 0) + goto out; + } + + imx93_clk_gate_do_hardware(hw, false); +out: + spin_unlock_irqrestore(gate->lock, flags); +} + +static int imx93_clk_gate_reg_is_enabled(struct imx93_clk_gate *gate) +{ + u32 val = readl(gate->reg + AUTHEN_OFFSET); + + if (val & CPULPM_EN) { + val = readl(gate->reg + LPM_CUR_OFFSET); + if (val == LPM_SETTING_ON) + return 1; + } else { + val = readl(gate->reg); + if (((val >> gate->bit_idx) & gate->mask) == gate->val) + return 1; + } + + return 0; +} + +static int imx93_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); + unsigned long flags; + int ret; + + spin_lock_irqsave(gate->lock, flags); + + ret = imx93_clk_gate_reg_is_enabled(gate); + + spin_unlock_irqrestore(gate->lock, flags); + + return ret; +} + +static void imx93_clk_gate_disable_unused(struct clk_hw *hw) +{ + struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); + unsigned long flags; + + spin_lock_irqsave(gate->lock, flags); + + if (!gate->share_count || *gate->share_count == 0) + imx93_clk_gate_do_hardware(hw, false); + + spin_unlock_irqrestore(gate->lock, flags); +} + +static const struct clk_ops imx93_clk_gate_ops = { + .enable = imx93_clk_gate_enable, + .disable = imx93_clk_gate_disable, + .disable_unused = imx93_clk_gate_disable_unused, + .is_enabled = imx93_clk_gate_is_enabled, +}; + +static const struct clk_ops imx93_clk_gate_ro_ops = { + .is_enabled = imx93_clk_gate_is_enabled, +}; + +struct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val, + u32 mask, u32 domain_id, unsigned int *share_count) +{ + struct imx93_clk_gate *gate; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + u32 authen; + + gate = kzalloc(sizeof(struct imx93_clk_gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = reg; + gate->lock = &imx_ccm_lock; + gate->bit_idx = bit_idx; + gate->val = val; + gate->mask = mask; + gate->share_count = share_count; + + init.name = name; + init.ops = &imx93_clk_gate_ops; + init.flags = flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + gate->hw.init = &init; + hw = &gate->hw; + + authen = readl(reg + AUTHEN_OFFSET); + if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id))) + init.ops = &imx93_clk_gate_ro_ops; + + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(gate); + return ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx93_clk_gate); diff --git a/drivers/clk/imx/clk-gate-exclusive.c b/drivers/clk/imx/clk-gate-exclusive.c new file mode 100644 index 000000000..77342893b --- /dev/null +++ b/drivers/clk/imx/clk-gate-exclusive.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2014 Freescale Semiconductor, Inc. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_gate_exclusive - i.MX specific gate clock which is mutually + * exclusive with other gate clocks + * + * @gate: the parent class + * @exclusive_mask: mask of gate bits which are mutually exclusive to this + * gate clock + * + * The imx exclusive gate clock is a subclass of basic clk_gate + * with an addtional mask to indicate which other gate bits in the same + * register is mutually exclusive to this gate clock. + */ +struct clk_gate_exclusive { + struct clk_gate gate; + u32 exclusive_mask; +}; + +static int clk_gate_exclusive_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct clk_gate_exclusive *exgate = container_of(gate, + struct clk_gate_exclusive, gate); + u32 val = readl(gate->reg); + + if (val & exgate->exclusive_mask) + return -EBUSY; + + return clk_gate_ops.enable(hw); +} + +static void clk_gate_exclusive_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int clk_gate_exclusive_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops clk_gate_exclusive_ops = { + .enable = clk_gate_exclusive_enable, + .disable = clk_gate_exclusive_disable, + .is_enabled = clk_gate_exclusive_is_enabled, +}; + +struct clk_hw *imx_clk_hw_gate_exclusive(const char *name, const char *parent, + void __iomem *reg, u8 shift, u32 exclusive_mask) +{ + struct clk_gate_exclusive *exgate; + struct clk_gate *gate; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (exclusive_mask == 0) + return ERR_PTR(-EINVAL); + + exgate = kzalloc(sizeof(*exgate), GFP_KERNEL); + if (!exgate) + return ERR_PTR(-ENOMEM); + gate = &exgate->gate; + + init.name = name; + init.ops = &clk_gate_exclusive_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent ? &parent : NULL; + init.num_parents = parent ? 1 : 0; + + gate->reg = reg; + gate->bit_idx = shift; + gate->lock = &imx_ccm_lock; + gate->hw.init = &init; + exgate->exclusive_mask = exclusive_mask; + + hw = &gate->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(gate); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c new file mode 100644 index 000000000..f16c4019f --- /dev/null +++ b/drivers/clk/imx/clk-gate2.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> + * + * Gated clock implementation + */ + +#include <linux/clk-provider.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/string.h> +#include "clk.h" + +/** + * DOC: basic gateable clock which can gate and ungate its output + * + * Traits of this clock: + * prepare - clk_(un)prepare only ensures parent is (un)prepared + * enable - clk_enable and clk_disable are functional & control gating + * rate - inherits rate from parent. No clk_set_rate support + * parent - fixed parent. No clk_set_parent support + */ + +struct clk_gate2 { + struct clk_hw hw; + void __iomem *reg; + u8 bit_idx; + u8 cgr_val; + u8 cgr_mask; + u8 flags; + spinlock_t *lock; + unsigned int *share_count; +}; + +#define to_clk_gate2(_hw) container_of(_hw, struct clk_gate2, hw) + +static void clk_gate2_do_shared_clks(struct clk_hw *hw, bool enable) +{ + struct clk_gate2 *gate = to_clk_gate2(hw); + u32 reg; + + reg = readl(gate->reg); + reg &= ~(gate->cgr_mask << gate->bit_idx); + if (enable) + reg |= (gate->cgr_val & gate->cgr_mask) << gate->bit_idx; + writel(reg, gate->reg); +} + +static int clk_gate2_enable(struct clk_hw *hw) +{ + struct clk_gate2 *gate = to_clk_gate2(hw); + unsigned long flags; + + spin_lock_irqsave(gate->lock, flags); + + if (gate->share_count && (*gate->share_count)++ > 0) + goto out; + + clk_gate2_do_shared_clks(hw, true); +out: + spin_unlock_irqrestore(gate->lock, flags); + + return 0; +} + +static void clk_gate2_disable(struct clk_hw *hw) +{ + struct clk_gate2 *gate = to_clk_gate2(hw); + unsigned long flags; + + spin_lock_irqsave(gate->lock, flags); + + if (gate->share_count) { + if (WARN_ON(*gate->share_count == 0)) + goto out; + else if (--(*gate->share_count) > 0) + goto out; + } + + clk_gate2_do_shared_clks(hw, false); +out: + spin_unlock_irqrestore(gate->lock, flags); +} + +static int clk_gate2_reg_is_enabled(void __iomem *reg, u8 bit_idx, + u8 cgr_val, u8 cgr_mask) +{ + u32 val = readl(reg); + + if (((val >> bit_idx) & cgr_mask) == cgr_val) + return 1; + + return 0; +} + +static int clk_gate2_is_enabled(struct clk_hw *hw) +{ + struct clk_gate2 *gate = to_clk_gate2(hw); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(gate->lock, flags); + + ret = clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx, + gate->cgr_val, gate->cgr_mask); + + spin_unlock_irqrestore(gate->lock, flags); + + return ret; +} + +static void clk_gate2_disable_unused(struct clk_hw *hw) +{ + struct clk_gate2 *gate = to_clk_gate2(hw); + unsigned long flags; + + spin_lock_irqsave(gate->lock, flags); + + if (!gate->share_count || *gate->share_count == 0) + clk_gate2_do_shared_clks(hw, false); + + spin_unlock_irqrestore(gate->lock, flags); +} + +static const struct clk_ops clk_gate2_ops = { + .enable = clk_gate2_enable, + .disable = clk_gate2_disable, + .disable_unused = clk_gate2_disable_unused, + .is_enabled = clk_gate2_is_enabled, +}; + +struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 cgr_mask, + u8 clk_gate2_flags, spinlock_t *lock, + unsigned int *share_count) +{ + struct clk_gate2 *gate; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + gate = kzalloc(sizeof(struct clk_gate2), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + /* struct clk_gate2 assignments */ + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->cgr_val = cgr_val; + gate->cgr_mask = cgr_mask; + gate->flags = clk_gate2_flags; + gate->lock = lock; + gate->share_count = share_count; + + init.name = name; + init.ops = &clk_gate2_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + gate->hw.init = &init; + hw = &gate->hw; + + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(gate); + return ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(clk_hw_register_gate2); diff --git a/drivers/clk/imx/clk-imx1.c b/drivers/clk/imx/clk-imx1.c new file mode 100644 index 000000000..22fc7491b --- /dev/null +++ b/drivers/clk/imx/clk-imx1.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix + */ + +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <dt-bindings/clock/imx1-clock.h> +#include <soc/imx/timer.h> +#include <asm/irq.h> + +#include "clk.h" + +#define MX1_CCM_BASE_ADDR 0x0021b000 +#define MX1_TIM1_BASE_ADDR 0x00220000 +#define MX1_TIM1_INT (NR_IRQS_LEGACY + 59) + +static const char *prem_sel_clks[] = { "clk32_premult", "clk16m", }; +static const char *clko_sel_clks[] = { "per1", "hclk", "clk48m", "clk16m", + "prem", "fclk", }; + +static struct clk *clk[IMX1_CLK_MAX]; +static struct clk_onecell_data clk_data; + +static void __iomem *ccm __initdata; +#define CCM_CSCR (ccm + 0x0000) +#define CCM_MPCTL0 (ccm + 0x0004) +#define CCM_SPCTL0 (ccm + 0x000c) +#define CCM_PCDR (ccm + 0x0020) +#define SCM_GCCR (ccm + 0x0810) + +static void __init mx1_clocks_init_dt(struct device_node *np) +{ + ccm = of_iomap(np, 0); + BUG_ON(!ccm); + + clk[IMX1_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[IMX1_CLK_CLK32] = imx_obtain_fixed_clock("clk32", 32768); + clk[IMX1_CLK_CLK16M_EXT] = imx_clk_fixed("clk16m_ext", 16000000); + clk[IMX1_CLK_CLK16M] = imx_clk_gate("clk16m", "clk16m_ext", CCM_CSCR, 17); + clk[IMX1_CLK_CLK32_PREMULT] = imx_clk_fixed_factor("clk32_premult", "clk32", 512, 1); + clk[IMX1_CLK_PREM] = imx_clk_mux("prem", CCM_CSCR, 16, 1, prem_sel_clks, ARRAY_SIZE(prem_sel_clks)); + clk[IMX1_CLK_MPLL] = imx_clk_pllv1(IMX_PLLV1_IMX1, "mpll", "clk32_premult", CCM_MPCTL0); + clk[IMX1_CLK_MPLL_GATE] = imx_clk_gate("mpll_gate", "mpll", CCM_CSCR, 0); + clk[IMX1_CLK_SPLL] = imx_clk_pllv1(IMX_PLLV1_IMX1, "spll", "prem", CCM_SPCTL0); + clk[IMX1_CLK_SPLL_GATE] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); + clk[IMX1_CLK_MCU] = imx_clk_divider("mcu", "clk32_premult", CCM_CSCR, 15, 1); + clk[IMX1_CLK_FCLK] = imx_clk_divider("fclk", "mpll_gate", CCM_CSCR, 15, 1); + clk[IMX1_CLK_HCLK] = imx_clk_divider("hclk", "spll_gate", CCM_CSCR, 10, 4); + clk[IMX1_CLK_CLK48M] = imx_clk_divider("clk48m", "spll_gate", CCM_CSCR, 26, 3); + clk[IMX1_CLK_PER1] = imx_clk_divider("per1", "spll_gate", CCM_PCDR, 0, 4); + clk[IMX1_CLK_PER2] = imx_clk_divider("per2", "spll_gate", CCM_PCDR, 4, 4); + clk[IMX1_CLK_PER3] = imx_clk_divider("per3", "spll_gate", CCM_PCDR, 16, 7); + clk[IMX1_CLK_CLKO] = imx_clk_mux("clko", CCM_CSCR, 29, 3, clko_sel_clks, ARRAY_SIZE(clko_sel_clks)); + clk[IMX1_CLK_UART3_GATE] = imx_clk_gate("uart3_gate", "hclk", SCM_GCCR, 6); + clk[IMX1_CLK_SSI2_GATE] = imx_clk_gate("ssi2_gate", "hclk", SCM_GCCR, 5); + clk[IMX1_CLK_BROM_GATE] = imx_clk_gate("brom_gate", "hclk", SCM_GCCR, 4); + clk[IMX1_CLK_DMA_GATE] = imx_clk_gate("dma_gate", "hclk", SCM_GCCR, 3); + clk[IMX1_CLK_CSI_GATE] = imx_clk_gate("csi_gate", "hclk", SCM_GCCR, 2); + clk[IMX1_CLK_MMA_GATE] = imx_clk_gate("mma_gate", "hclk", SCM_GCCR, 1); + clk[IMX1_CLK_USBD_GATE] = imx_clk_gate("usbd_gate", "clk48m", SCM_GCCR, 0); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(imx1_ccm, "fsl,imx1-ccm", mx1_clocks_init_dt); diff --git a/drivers/clk/imx/clk-imx25.c b/drivers/clk/imx/clk-imx25.c new file mode 100644 index 000000000..66192fe0a --- /dev/null +++ b/drivers/clk/imx/clk-imx25.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2009 by Sascha Hauer, Pengutronix + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#include "clk.h" + +#define CCM_MPCTL 0x00 +#define CCM_UPCTL 0x04 +#define CCM_CCTL 0x08 +#define CCM_CGCR0 0x0C +#define CCM_CGCR1 0x10 +#define CCM_CGCR2 0x14 +#define CCM_PCDR0 0x18 +#define CCM_PCDR1 0x1C +#define CCM_PCDR2 0x20 +#define CCM_PCDR3 0x24 +#define CCM_RCSR 0x28 +#define CCM_CRDR 0x2C +#define CCM_DCVR0 0x30 +#define CCM_DCVR1 0x34 +#define CCM_DCVR2 0x38 +#define CCM_DCVR3 0x3c +#define CCM_LTR0 0x40 +#define CCM_LTR1 0x44 +#define CCM_LTR2 0x48 +#define CCM_LTR3 0x4c +#define CCM_MCR 0x64 + +#define ccm(x) (ccm_base + (x)) + +static struct clk_onecell_data clk_data; + +static const char *cpu_sel_clks[] = { "mpll", "mpll_cpu_3_4", }; +static const char *per_sel_clks[] = { "ahb", "upll", }; +static const char *cko_sel_clks[] = { "dummy", "osc", "cpu", "ahb", + "ipg", "dummy", "dummy", "dummy", + "dummy", "dummy", "per0", "per2", + "per13", "per14", "usbotg_ahb", "dummy",}; + +enum mx25_clks { + dummy, osc, mpll, upll, mpll_cpu_3_4, cpu_sel, cpu, ahb, usb_div, ipg, + per0_sel, per1_sel, per2_sel, per3_sel, per4_sel, per5_sel, per6_sel, + per7_sel, per8_sel, per9_sel, per10_sel, per11_sel, per12_sel, + per13_sel, per14_sel, per15_sel, per0, per1, per2, per3, per4, per5, + per6, per7, per8, per9, per10, per11, per12, per13, per14, per15, + csi_ipg_per, epit_ipg_per, esai_ipg_per, esdhc1_ipg_per, esdhc2_ipg_per, + gpt_ipg_per, i2c_ipg_per, lcdc_ipg_per, nfc_ipg_per, owire_ipg_per, + pwm_ipg_per, sim1_ipg_per, sim2_ipg_per, ssi1_ipg_per, ssi2_ipg_per, + uart_ipg_per, ata_ahb, reserved1, csi_ahb, emi_ahb, esai_ahb, esdhc1_ahb, + esdhc2_ahb, fec_ahb, lcdc_ahb, rtic_ahb, sdma_ahb, slcdc_ahb, usbotg_ahb, + reserved2, reserved3, reserved4, reserved5, can1_ipg, can2_ipg, csi_ipg, + cspi1_ipg, cspi2_ipg, cspi3_ipg, dryice_ipg, ect_ipg, epit1_ipg, epit2_ipg, + reserved6, esdhc1_ipg, esdhc2_ipg, fec_ipg, reserved7, reserved8, reserved9, + gpt1_ipg, gpt2_ipg, gpt3_ipg, gpt4_ipg, reserved10, reserved11, reserved12, + iim_ipg, reserved13, reserved14, kpp_ipg, lcdc_ipg, reserved15, pwm1_ipg, + pwm2_ipg, pwm3_ipg, pwm4_ipg, rngb_ipg, reserved16, scc_ipg, sdma_ipg, + sim1_ipg, sim2_ipg, slcdc_ipg, spba_ipg, ssi1_ipg, ssi2_ipg, tsc_ipg, + uart1_ipg, uart2_ipg, uart3_ipg, uart4_ipg, uart5_ipg, reserved17, + wdt_ipg, cko_div, cko_sel, cko, clk_max +}; + +static struct clk *clk[clk_max]; + +static int __init __mx25_clocks_init(void __iomem *ccm_base) +{ + BUG_ON(!ccm_base); + + clk[dummy] = imx_clk_fixed("dummy", 0); + clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX25, "mpll", "osc", ccm(CCM_MPCTL)); + clk[upll] = imx_clk_pllv1(IMX_PLLV1_IMX25, "upll", "osc", ccm(CCM_UPCTL)); + clk[mpll_cpu_3_4] = imx_clk_fixed_factor("mpll_cpu_3_4", "mpll", 3, 4); + clk[cpu_sel] = imx_clk_mux("cpu_sel", ccm(CCM_CCTL), 14, 1, cpu_sel_clks, ARRAY_SIZE(cpu_sel_clks)); + clk[cpu] = imx_clk_divider("cpu", "cpu_sel", ccm(CCM_CCTL), 30, 2); + clk[ahb] = imx_clk_divider("ahb", "cpu", ccm(CCM_CCTL), 28, 2); + clk[usb_div] = imx_clk_divider("usb_div", "upll", ccm(CCM_CCTL), 16, 6); + clk[ipg] = imx_clk_fixed_factor("ipg", "ahb", 1, 2); + clk[per0_sel] = imx_clk_mux("per0_sel", ccm(CCM_MCR), 0, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per1_sel] = imx_clk_mux("per1_sel", ccm(CCM_MCR), 1, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per2_sel] = imx_clk_mux("per2_sel", ccm(CCM_MCR), 2, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per3_sel] = imx_clk_mux("per3_sel", ccm(CCM_MCR), 3, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per4_sel] = imx_clk_mux("per4_sel", ccm(CCM_MCR), 4, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per5_sel] = imx_clk_mux("per5_sel", ccm(CCM_MCR), 5, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per6_sel] = imx_clk_mux("per6_sel", ccm(CCM_MCR), 6, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per7_sel] = imx_clk_mux("per7_sel", ccm(CCM_MCR), 7, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per8_sel] = imx_clk_mux("per8_sel", ccm(CCM_MCR), 8, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per9_sel] = imx_clk_mux("per9_sel", ccm(CCM_MCR), 9, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per10_sel] = imx_clk_mux("per10_sel", ccm(CCM_MCR), 10, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per11_sel] = imx_clk_mux("per11_sel", ccm(CCM_MCR), 11, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per12_sel] = imx_clk_mux("per12_sel", ccm(CCM_MCR), 12, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per13_sel] = imx_clk_mux("per13_sel", ccm(CCM_MCR), 13, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per14_sel] = imx_clk_mux("per14_sel", ccm(CCM_MCR), 14, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[per15_sel] = imx_clk_mux("per15_sel", ccm(CCM_MCR), 15, 1, per_sel_clks, ARRAY_SIZE(per_sel_clks)); + clk[cko_div] = imx_clk_divider("cko_div", "cko_sel", ccm(CCM_MCR), 24, 6); + clk[cko_sel] = imx_clk_mux("cko_sel", ccm(CCM_MCR), 20, 4, cko_sel_clks, ARRAY_SIZE(cko_sel_clks)); + clk[cko] = imx_clk_gate("cko", "cko_div", ccm(CCM_MCR), 30); + clk[per0] = imx_clk_divider("per0", "per0_sel", ccm(CCM_PCDR0), 0, 6); + clk[per1] = imx_clk_divider("per1", "per1_sel", ccm(CCM_PCDR0), 8, 6); + clk[per2] = imx_clk_divider("per2", "per2_sel", ccm(CCM_PCDR0), 16, 6); + clk[per3] = imx_clk_divider("per3", "per3_sel", ccm(CCM_PCDR0), 24, 6); + clk[per4] = imx_clk_divider("per4", "per4_sel", ccm(CCM_PCDR1), 0, 6); + clk[per5] = imx_clk_divider("per5", "per5_sel", ccm(CCM_PCDR1), 8, 6); + clk[per6] = imx_clk_divider("per6", "per6_sel", ccm(CCM_PCDR1), 16, 6); + clk[per7] = imx_clk_divider("per7", "per7_sel", ccm(CCM_PCDR1), 24, 6); + clk[per8] = imx_clk_divider("per8", "per8_sel", ccm(CCM_PCDR2), 0, 6); + clk[per9] = imx_clk_divider("per9", "per9_sel", ccm(CCM_PCDR2), 8, 6); + clk[per10] = imx_clk_divider("per10", "per10_sel", ccm(CCM_PCDR2), 16, 6); + clk[per11] = imx_clk_divider("per11", "per11_sel", ccm(CCM_PCDR2), 24, 6); + clk[per12] = imx_clk_divider("per12", "per12_sel", ccm(CCM_PCDR3), 0, 6); + clk[per13] = imx_clk_divider("per13", "per13_sel", ccm(CCM_PCDR3), 8, 6); + clk[per14] = imx_clk_divider("per14", "per14_sel", ccm(CCM_PCDR3), 16, 6); + clk[per15] = imx_clk_divider("per15", "per15_sel", ccm(CCM_PCDR3), 24, 6); + clk[csi_ipg_per] = imx_clk_gate("csi_ipg_per", "per0", ccm(CCM_CGCR0), 0); + clk[epit_ipg_per] = imx_clk_gate("epit_ipg_per", "per1", ccm(CCM_CGCR0), 1); + clk[esai_ipg_per] = imx_clk_gate("esai_ipg_per", "per2", ccm(CCM_CGCR0), 2); + clk[esdhc1_ipg_per] = imx_clk_gate("esdhc1_ipg_per", "per3", ccm(CCM_CGCR0), 3); + clk[esdhc2_ipg_per] = imx_clk_gate("esdhc2_ipg_per", "per4", ccm(CCM_CGCR0), 4); + clk[gpt_ipg_per] = imx_clk_gate("gpt_ipg_per", "per5", ccm(CCM_CGCR0), 5); + clk[i2c_ipg_per] = imx_clk_gate("i2c_ipg_per", "per6", ccm(CCM_CGCR0), 6); + clk[lcdc_ipg_per] = imx_clk_gate("lcdc_ipg_per", "per7", ccm(CCM_CGCR0), 7); + clk[nfc_ipg_per] = imx_clk_gate("nfc_ipg_per", "per8", ccm(CCM_CGCR0), 8); + clk[owire_ipg_per] = imx_clk_gate("owire_ipg_per", "per9", ccm(CCM_CGCR0), 9); + clk[pwm_ipg_per] = imx_clk_gate("pwm_ipg_per", "per10", ccm(CCM_CGCR0), 10); + clk[sim1_ipg_per] = imx_clk_gate("sim1_ipg_per", "per11", ccm(CCM_CGCR0), 11); + clk[sim2_ipg_per] = imx_clk_gate("sim2_ipg_per", "per12", ccm(CCM_CGCR0), 12); + clk[ssi1_ipg_per] = imx_clk_gate("ssi1_ipg_per", "per13", ccm(CCM_CGCR0), 13); + clk[ssi2_ipg_per] = imx_clk_gate("ssi2_ipg_per", "per14", ccm(CCM_CGCR0), 14); + clk[uart_ipg_per] = imx_clk_gate("uart_ipg_per", "per15", ccm(CCM_CGCR0), 15); + clk[ata_ahb] = imx_clk_gate("ata_ahb", "ahb", ccm(CCM_CGCR0), 16); + /* CCM_CGCR0(17): reserved */ + clk[csi_ahb] = imx_clk_gate("csi_ahb", "ahb", ccm(CCM_CGCR0), 18); + clk[emi_ahb] = imx_clk_gate("emi_ahb", "ahb", ccm(CCM_CGCR0), 19); + clk[esai_ahb] = imx_clk_gate("esai_ahb", "ahb", ccm(CCM_CGCR0), 20); + clk[esdhc1_ahb] = imx_clk_gate("esdhc1_ahb", "ahb", ccm(CCM_CGCR0), 21); + clk[esdhc2_ahb] = imx_clk_gate("esdhc2_ahb", "ahb", ccm(CCM_CGCR0), 22); + clk[fec_ahb] = imx_clk_gate("fec_ahb", "ahb", ccm(CCM_CGCR0), 23); + clk[lcdc_ahb] = imx_clk_gate("lcdc_ahb", "ahb", ccm(CCM_CGCR0), 24); + clk[rtic_ahb] = imx_clk_gate("rtic_ahb", "ahb", ccm(CCM_CGCR0), 25); + clk[sdma_ahb] = imx_clk_gate("sdma_ahb", "ahb", ccm(CCM_CGCR0), 26); + clk[slcdc_ahb] = imx_clk_gate("slcdc_ahb", "ahb", ccm(CCM_CGCR0), 27); + clk[usbotg_ahb] = imx_clk_gate("usbotg_ahb", "ahb", ccm(CCM_CGCR0), 28); + /* CCM_CGCR0(29-31): reserved */ + /* CCM_CGCR1(0): reserved in datasheet, used as audmux in FSL kernel */ + clk[can1_ipg] = imx_clk_gate("can1_ipg", "ipg", ccm(CCM_CGCR1), 2); + clk[can2_ipg] = imx_clk_gate("can2_ipg", "ipg", ccm(CCM_CGCR1), 3); + clk[csi_ipg] = imx_clk_gate("csi_ipg", "ipg", ccm(CCM_CGCR1), 4); + clk[cspi1_ipg] = imx_clk_gate("cspi1_ipg", "ipg", ccm(CCM_CGCR1), 5); + clk[cspi2_ipg] = imx_clk_gate("cspi2_ipg", "ipg", ccm(CCM_CGCR1), 6); + clk[cspi3_ipg] = imx_clk_gate("cspi3_ipg", "ipg", ccm(CCM_CGCR1), 7); + clk[dryice_ipg] = imx_clk_gate("dryice_ipg", "ipg", ccm(CCM_CGCR1), 8); + clk[ect_ipg] = imx_clk_gate("ect_ipg", "ipg", ccm(CCM_CGCR1), 9); + clk[epit1_ipg] = imx_clk_gate("epit1_ipg", "ipg", ccm(CCM_CGCR1), 10); + clk[epit2_ipg] = imx_clk_gate("epit2_ipg", "ipg", ccm(CCM_CGCR1), 11); + /* CCM_CGCR1(12): reserved in datasheet, used as esai in FSL kernel */ + clk[esdhc1_ipg] = imx_clk_gate("esdhc1_ipg", "ipg", ccm(CCM_CGCR1), 13); + clk[esdhc2_ipg] = imx_clk_gate("esdhc2_ipg", "ipg", ccm(CCM_CGCR1), 14); + clk[fec_ipg] = imx_clk_gate("fec_ipg", "ipg", ccm(CCM_CGCR1), 15); + /* CCM_CGCR1(16): reserved in datasheet, used as gpio1 in FSL kernel */ + /* CCM_CGCR1(17): reserved in datasheet, used as gpio2 in FSL kernel */ + /* CCM_CGCR1(18): reserved in datasheet, used as gpio3 in FSL kernel */ + clk[gpt1_ipg] = imx_clk_gate("gpt1_ipg", "ipg", ccm(CCM_CGCR1), 19); + clk[gpt2_ipg] = imx_clk_gate("gpt2_ipg", "ipg", ccm(CCM_CGCR1), 20); + clk[gpt3_ipg] = imx_clk_gate("gpt3_ipg", "ipg", ccm(CCM_CGCR1), 21); + clk[gpt4_ipg] = imx_clk_gate("gpt4_ipg", "ipg", ccm(CCM_CGCR1), 22); + /* CCM_CGCR1(23): reserved in datasheet, used as i2c1 in FSL kernel */ + /* CCM_CGCR1(24): reserved in datasheet, used as i2c2 in FSL kernel */ + /* CCM_CGCR1(25): reserved in datasheet, used as i2c3 in FSL kernel */ + clk[iim_ipg] = imx_clk_gate("iim_ipg", "ipg", ccm(CCM_CGCR1), 26); + /* CCM_CGCR1(27): reserved in datasheet, used as iomuxc in FSL kernel */ + /* CCM_CGCR1(28): reserved in datasheet, used as kpp in FSL kernel */ + clk[kpp_ipg] = imx_clk_gate("kpp_ipg", "ipg", ccm(CCM_CGCR1), 28); + clk[lcdc_ipg] = imx_clk_gate("lcdc_ipg", "ipg", ccm(CCM_CGCR1), 29); + /* CCM_CGCR1(30): reserved in datasheet, used as owire in FSL kernel */ + clk[pwm1_ipg] = imx_clk_gate("pwm1_ipg", "ipg", ccm(CCM_CGCR1), 31); + clk[pwm2_ipg] = imx_clk_gate("pwm2_ipg", "ipg", ccm(CCM_CGCR2), 0); + clk[pwm3_ipg] = imx_clk_gate("pwm3_ipg", "ipg", ccm(CCM_CGCR2), 1); + clk[pwm4_ipg] = imx_clk_gate("pwm4_ipg", "ipg", ccm(CCM_CGCR2), 2); + clk[rngb_ipg] = imx_clk_gate("rngb_ipg", "ipg", ccm(CCM_CGCR2), 3); + /* CCM_CGCR2(4): reserved in datasheet, used as rtic in FSL kernel */ + clk[scc_ipg] = imx_clk_gate("scc_ipg", "ipg", ccm(CCM_CGCR2), 5); + clk[sdma_ipg] = imx_clk_gate("sdma_ipg", "ipg", ccm(CCM_CGCR2), 6); + clk[sim1_ipg] = imx_clk_gate("sim1_ipg", "ipg", ccm(CCM_CGCR2), 7); + clk[sim2_ipg] = imx_clk_gate("sim2_ipg", "ipg", ccm(CCM_CGCR2), 8); + clk[slcdc_ipg] = imx_clk_gate("slcdc_ipg", "ipg", ccm(CCM_CGCR2), 9); + clk[spba_ipg] = imx_clk_gate("spba_ipg", "ipg", ccm(CCM_CGCR2), 10); + clk[ssi1_ipg] = imx_clk_gate("ssi1_ipg", "ipg", ccm(CCM_CGCR2), 11); + clk[ssi2_ipg] = imx_clk_gate("ssi2_ipg", "ipg", ccm(CCM_CGCR2), 12); + clk[tsc_ipg] = imx_clk_gate("tsc_ipg", "ipg", ccm(CCM_CGCR2), 13); + clk[uart1_ipg] = imx_clk_gate("uart1_ipg", "ipg", ccm(CCM_CGCR2), 14); + clk[uart2_ipg] = imx_clk_gate("uart2_ipg", "ipg", ccm(CCM_CGCR2), 15); + clk[uart3_ipg] = imx_clk_gate("uart3_ipg", "ipg", ccm(CCM_CGCR2), 16); + clk[uart4_ipg] = imx_clk_gate("uart4_ipg", "ipg", ccm(CCM_CGCR2), 17); + clk[uart5_ipg] = imx_clk_gate("uart5_ipg", "ipg", ccm(CCM_CGCR2), 18); + /* CCM_CGCR2(19): reserved in datasheet, but used as wdt in FSL kernel */ + clk[wdt_ipg] = imx_clk_gate("wdt_ipg", "ipg", ccm(CCM_CGCR2), 19); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_prepare_enable(clk[emi_ahb]); + + /* Clock source for gpt must be derived from AHB */ + clk_set_parent(clk[per5_sel], clk[ahb]); + + /* + * Let's initially set up CLKO parent as ipg, since this configuration + * is used on some imx25 board designs to clock the audio codec. + */ + clk_set_parent(clk[cko_sel], clk[ipg]); + + imx_register_uart_clocks(6); + + return 0; +} + +static void __init mx25_clocks_init_dt(struct device_node *np) +{ + void __iomem *ccm; + + ccm = of_iomap(np, 0); + __mx25_clocks_init(ccm); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(imx25_ccm, "fsl,imx25-ccm", mx25_clocks_init_dt); diff --git a/drivers/clk/imx/clk-imx27.c b/drivers/clk/imx/clk-imx27.c new file mode 100644 index 000000000..56a5fc402 --- /dev/null +++ b/drivers/clk/imx/clk-imx27.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <dt-bindings/clock/imx27-clock.h> +#include <soc/imx/revision.h> +#include <soc/imx/timer.h> +#include <asm/irq.h> + +#include "clk.h" + +#define MX27_CCM_BASE_ADDR 0x10027000 +#define MX27_GPT1_BASE_ADDR 0x10003000 +#define MX27_INT_GPT1 (NR_IRQS_LEGACY + 26) + +static void __iomem *ccm __initdata; + +/* Register offsets */ +#define CCM_CSCR (ccm + 0x00) +#define CCM_MPCTL0 (ccm + 0x04) +#define CCM_MPCTL1 (ccm + 0x08) +#define CCM_SPCTL0 (ccm + 0x0c) +#define CCM_SPCTL1 (ccm + 0x10) +#define CCM_PCDR0 (ccm + 0x18) +#define CCM_PCDR1 (ccm + 0x1c) +#define CCM_PCCR0 (ccm + 0x20) +#define CCM_PCCR1 (ccm + 0x24) +#define CCM_CCSR (ccm + 0x28) + +static const char *vpu_sel_clks[] = { "spll", "mpll_main2", }; +static const char *cpu_sel_clks[] = { "mpll_main2", "mpll", }; +static const char *mpll_sel_clks[] = { "fpm", "mpll_osc_sel", }; +static const char *mpll_osc_sel_clks[] = { "ckih_gate", "ckih_div1p5", }; +static const char *clko_sel_clks[] = { + "ckil", "fpm", "ckih_gate", "ckih_gate", + "ckih_gate", "mpll", "spll", "cpu_div", + "ahb", "ipg", "per1_div", "per2_div", + "per3_div", "per4_div", "ssi1_div", "ssi2_div", + "nfc_div", "mshc_div", "vpu_div", "60m", + "32k", "usb_div", "dptc", +}; + +static const char *ssi_sel_clks[] = { "spll_gate", "mpll", }; + +static struct clk *clk[IMX27_CLK_MAX]; +static struct clk_onecell_data clk_data; + +static void __init _mx27_clocks_init(unsigned long fref) +{ + BUG_ON(!ccm); + + clk[IMX27_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[IMX27_CLK_CKIH] = imx_clk_fixed("ckih", fref); + clk[IMX27_CLK_CKIL] = imx_clk_fixed("ckil", 32768); + clk[IMX27_CLK_FPM] = imx_clk_fixed_factor("fpm", "ckil", 1024, 1); + clk[IMX27_CLK_CKIH_DIV1P5] = imx_clk_fixed_factor("ckih_div1p5", "ckih_gate", 2, 3); + clk[IMX27_CLK_CKIH_GATE] = imx_clk_gate_dis("ckih_gate", "ckih", CCM_CSCR, 3); + clk[IMX27_CLK_MPLL_OSC_SEL] = imx_clk_mux("mpll_osc_sel", CCM_CSCR, 4, 1, mpll_osc_sel_clks, ARRAY_SIZE(mpll_osc_sel_clks)); + clk[IMX27_CLK_MPLL_SEL] = imx_clk_mux("mpll_sel", CCM_CSCR, 16, 1, mpll_sel_clks, ARRAY_SIZE(mpll_sel_clks)); + clk[IMX27_CLK_MPLL] = imx_clk_pllv1(IMX_PLLV1_IMX27, "mpll", "mpll_sel", CCM_MPCTL0); + clk[IMX27_CLK_SPLL] = imx_clk_pllv1(IMX_PLLV1_IMX27, "spll", "ckih_gate", CCM_SPCTL0); + clk[IMX27_CLK_SPLL_GATE] = imx_clk_gate("spll_gate", "spll", CCM_CSCR, 1); + clk[IMX27_CLK_MPLL_MAIN2] = imx_clk_fixed_factor("mpll_main2", "mpll", 2, 3); + + if (mx27_revision() >= IMX_CHIP_REVISION_2_0) { + clk[IMX27_CLK_AHB] = imx_clk_divider("ahb", "mpll_main2", CCM_CSCR, 8, 2); + clk[IMX27_CLK_IPG] = imx_clk_fixed_factor("ipg", "ahb", 1, 2); + } else { + clk[IMX27_CLK_AHB] = imx_clk_divider("ahb", "mpll_main2", CCM_CSCR, 9, 4); + clk[IMX27_CLK_IPG] = imx_clk_divider("ipg", "ahb", CCM_CSCR, 8, 1); + } + + clk[IMX27_CLK_MSHC_DIV] = imx_clk_divider("mshc_div", "ahb", CCM_PCDR0, 0, 6); + clk[IMX27_CLK_NFC_DIV] = imx_clk_divider("nfc_div", "ahb", CCM_PCDR0, 6, 4); + clk[IMX27_CLK_PER1_DIV] = imx_clk_divider("per1_div", "mpll_main2", CCM_PCDR1, 0, 6); + clk[IMX27_CLK_PER2_DIV] = imx_clk_divider("per2_div", "mpll_main2", CCM_PCDR1, 8, 6); + clk[IMX27_CLK_PER3_DIV] = imx_clk_divider("per3_div", "mpll_main2", CCM_PCDR1, 16, 6); + clk[IMX27_CLK_PER4_DIV] = imx_clk_divider("per4_div", "mpll_main2", CCM_PCDR1, 24, 6); + clk[IMX27_CLK_VPU_SEL] = imx_clk_mux("vpu_sel", CCM_CSCR, 21, 1, vpu_sel_clks, ARRAY_SIZE(vpu_sel_clks)); + clk[IMX27_CLK_VPU_DIV] = imx_clk_divider("vpu_div", "vpu_sel", CCM_PCDR0, 10, 6); + clk[IMX27_CLK_USB_DIV] = imx_clk_divider("usb_div", "spll_gate", CCM_CSCR, 28, 3); + clk[IMX27_CLK_CPU_SEL] = imx_clk_mux("cpu_sel", CCM_CSCR, 15, 1, cpu_sel_clks, ARRAY_SIZE(cpu_sel_clks)); + clk[IMX27_CLK_CLKO_SEL] = imx_clk_mux("clko_sel", CCM_CCSR, 0, 5, clko_sel_clks, ARRAY_SIZE(clko_sel_clks)); + + if (mx27_revision() >= IMX_CHIP_REVISION_2_0) + clk[IMX27_CLK_CPU_DIV] = imx_clk_divider("cpu_div", "cpu_sel", CCM_CSCR, 12, 2); + else + clk[IMX27_CLK_CPU_DIV] = imx_clk_divider("cpu_div", "cpu_sel", CCM_CSCR, 13, 3); + + clk[IMX27_CLK_CLKO_DIV] = imx_clk_divider("clko_div", "clko_sel", CCM_PCDR0, 22, 3); + clk[IMX27_CLK_SSI1_SEL] = imx_clk_mux("ssi1_sel", CCM_CSCR, 22, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); + clk[IMX27_CLK_SSI2_SEL] = imx_clk_mux("ssi2_sel", CCM_CSCR, 23, 1, ssi_sel_clks, ARRAY_SIZE(ssi_sel_clks)); + clk[IMX27_CLK_SSI1_DIV] = imx_clk_divider("ssi1_div", "ssi1_sel", CCM_PCDR0, 16, 6); + clk[IMX27_CLK_SSI2_DIV] = imx_clk_divider("ssi2_div", "ssi2_sel", CCM_PCDR0, 26, 6); + clk[IMX27_CLK_CLKO_EN] = imx_clk_gate("clko_en", "clko_div", CCM_PCCR0, 0); + clk[IMX27_CLK_SSI2_IPG_GATE] = imx_clk_gate("ssi2_ipg_gate", "ipg", CCM_PCCR0, 0); + clk[IMX27_CLK_SSI1_IPG_GATE] = imx_clk_gate("ssi1_ipg_gate", "ipg", CCM_PCCR0, 1); + clk[IMX27_CLK_SLCDC_IPG_GATE] = imx_clk_gate("slcdc_ipg_gate", "ipg", CCM_PCCR0, 2); + clk[IMX27_CLK_SDHC3_IPG_GATE] = imx_clk_gate("sdhc3_ipg_gate", "ipg", CCM_PCCR0, 3); + clk[IMX27_CLK_SDHC2_IPG_GATE] = imx_clk_gate("sdhc2_ipg_gate", "ipg", CCM_PCCR0, 4); + clk[IMX27_CLK_SDHC1_IPG_GATE] = imx_clk_gate("sdhc1_ipg_gate", "ipg", CCM_PCCR0, 5); + clk[IMX27_CLK_SCC_IPG_GATE] = imx_clk_gate("scc_ipg_gate", "ipg", CCM_PCCR0, 6); + clk[IMX27_CLK_SAHARA_IPG_GATE] = imx_clk_gate("sahara_ipg_gate", "ipg", CCM_PCCR0, 7); + clk[IMX27_CLK_RTIC_IPG_GATE] = imx_clk_gate("rtic_ipg_gate", "ipg", CCM_PCCR0, 8); + clk[IMX27_CLK_RTC_IPG_GATE] = imx_clk_gate("rtc_ipg_gate", "ipg", CCM_PCCR0, 9); + clk[IMX27_CLK_PWM_IPG_GATE] = imx_clk_gate("pwm_ipg_gate", "ipg", CCM_PCCR0, 11); + clk[IMX27_CLK_OWIRE_IPG_GATE] = imx_clk_gate("owire_ipg_gate", "ipg", CCM_PCCR0, 12); + clk[IMX27_CLK_MSHC_IPG_GATE] = imx_clk_gate("mshc_ipg_gate", "ipg", CCM_PCCR0, 13); + clk[IMX27_CLK_LCDC_IPG_GATE] = imx_clk_gate("lcdc_ipg_gate", "ipg", CCM_PCCR0, 14); + clk[IMX27_CLK_KPP_IPG_GATE] = imx_clk_gate("kpp_ipg_gate", "ipg", CCM_PCCR0, 15); + clk[IMX27_CLK_IIM_IPG_GATE] = imx_clk_gate("iim_ipg_gate", "ipg", CCM_PCCR0, 16); + clk[IMX27_CLK_I2C2_IPG_GATE] = imx_clk_gate("i2c2_ipg_gate", "ipg", CCM_PCCR0, 17); + clk[IMX27_CLK_I2C1_IPG_GATE] = imx_clk_gate("i2c1_ipg_gate", "ipg", CCM_PCCR0, 18); + clk[IMX27_CLK_GPT6_IPG_GATE] = imx_clk_gate("gpt6_ipg_gate", "ipg", CCM_PCCR0, 19); + clk[IMX27_CLK_GPT5_IPG_GATE] = imx_clk_gate("gpt5_ipg_gate", "ipg", CCM_PCCR0, 20); + clk[IMX27_CLK_GPT4_IPG_GATE] = imx_clk_gate("gpt4_ipg_gate", "ipg", CCM_PCCR0, 21); + clk[IMX27_CLK_GPT3_IPG_GATE] = imx_clk_gate("gpt3_ipg_gate", "ipg", CCM_PCCR0, 22); + clk[IMX27_CLK_GPT2_IPG_GATE] = imx_clk_gate("gpt2_ipg_gate", "ipg", CCM_PCCR0, 23); + clk[IMX27_CLK_GPT1_IPG_GATE] = imx_clk_gate("gpt1_ipg_gate", "ipg", CCM_PCCR0, 24); + clk[IMX27_CLK_GPIO_IPG_GATE] = imx_clk_gate("gpio_ipg_gate", "ipg", CCM_PCCR0, 25); + clk[IMX27_CLK_FEC_IPG_GATE] = imx_clk_gate("fec_ipg_gate", "ipg", CCM_PCCR0, 26); + clk[IMX27_CLK_EMMA_IPG_GATE] = imx_clk_gate("emma_ipg_gate", "ipg", CCM_PCCR0, 27); + clk[IMX27_CLK_DMA_IPG_GATE] = imx_clk_gate("dma_ipg_gate", "ipg", CCM_PCCR0, 28); + clk[IMX27_CLK_CSPI3_IPG_GATE] = imx_clk_gate("cspi3_ipg_gate", "ipg", CCM_PCCR0, 29); + clk[IMX27_CLK_CSPI2_IPG_GATE] = imx_clk_gate("cspi2_ipg_gate", "ipg", CCM_PCCR0, 30); + clk[IMX27_CLK_CSPI1_IPG_GATE] = imx_clk_gate("cspi1_ipg_gate", "ipg", CCM_PCCR0, 31); + clk[IMX27_CLK_MSHC_BAUD_GATE] = imx_clk_gate("mshc_baud_gate", "mshc_div", CCM_PCCR1, 2); + clk[IMX27_CLK_NFC_BAUD_GATE] = imx_clk_gate("nfc_baud_gate", "nfc_div", CCM_PCCR1, 3); + clk[IMX27_CLK_SSI2_BAUD_GATE] = imx_clk_gate("ssi2_baud_gate", "ssi2_div", CCM_PCCR1, 4); + clk[IMX27_CLK_SSI1_BAUD_GATE] = imx_clk_gate("ssi1_baud_gate", "ssi1_div", CCM_PCCR1, 5); + clk[IMX27_CLK_VPU_BAUD_GATE] = imx_clk_gate("vpu_baud_gate", "vpu_div", CCM_PCCR1, 6); + clk[IMX27_CLK_PER4_GATE] = imx_clk_gate("per4_gate", "per4_div", CCM_PCCR1, 7); + clk[IMX27_CLK_PER3_GATE] = imx_clk_gate("per3_gate", "per3_div", CCM_PCCR1, 8); + clk[IMX27_CLK_PER2_GATE] = imx_clk_gate("per2_gate", "per2_div", CCM_PCCR1, 9); + clk[IMX27_CLK_PER1_GATE] = imx_clk_gate("per1_gate", "per1_div", CCM_PCCR1, 10); + clk[IMX27_CLK_USB_AHB_GATE] = imx_clk_gate("usb_ahb_gate", "ahb", CCM_PCCR1, 11); + clk[IMX27_CLK_SLCDC_AHB_GATE] = imx_clk_gate("slcdc_ahb_gate", "ahb", CCM_PCCR1, 12); + clk[IMX27_CLK_SAHARA_AHB_GATE] = imx_clk_gate("sahara_ahb_gate", "ahb", CCM_PCCR1, 13); + clk[IMX27_CLK_RTIC_AHB_GATE] = imx_clk_gate("rtic_ahb_gate", "ahb", CCM_PCCR1, 14); + clk[IMX27_CLK_LCDC_AHB_GATE] = imx_clk_gate("lcdc_ahb_gate", "ahb", CCM_PCCR1, 15); + clk[IMX27_CLK_VPU_AHB_GATE] = imx_clk_gate("vpu_ahb_gate", "ahb", CCM_PCCR1, 16); + clk[IMX27_CLK_FEC_AHB_GATE] = imx_clk_gate("fec_ahb_gate", "ahb", CCM_PCCR1, 17); + clk[IMX27_CLK_EMMA_AHB_GATE] = imx_clk_gate("emma_ahb_gate", "ahb", CCM_PCCR1, 18); + clk[IMX27_CLK_EMI_AHB_GATE] = imx_clk_gate("emi_ahb_gate", "ahb", CCM_PCCR1, 19); + clk[IMX27_CLK_DMA_AHB_GATE] = imx_clk_gate("dma_ahb_gate", "ahb", CCM_PCCR1, 20); + clk[IMX27_CLK_CSI_AHB_GATE] = imx_clk_gate("csi_ahb_gate", "ahb", CCM_PCCR1, 21); + clk[IMX27_CLK_BROM_AHB_GATE] = imx_clk_gate("brom_ahb_gate", "ahb", CCM_PCCR1, 22); + clk[IMX27_CLK_ATA_AHB_GATE] = imx_clk_gate("ata_ahb_gate", "ahb", CCM_PCCR1, 23); + clk[IMX27_CLK_WDOG_IPG_GATE] = imx_clk_gate("wdog_ipg_gate", "ipg", CCM_PCCR1, 24); + clk[IMX27_CLK_USB_IPG_GATE] = imx_clk_gate("usb_ipg_gate", "ipg", CCM_PCCR1, 25); + clk[IMX27_CLK_UART6_IPG_GATE] = imx_clk_gate("uart6_ipg_gate", "ipg", CCM_PCCR1, 26); + clk[IMX27_CLK_UART5_IPG_GATE] = imx_clk_gate("uart5_ipg_gate", "ipg", CCM_PCCR1, 27); + clk[IMX27_CLK_UART4_IPG_GATE] = imx_clk_gate("uart4_ipg_gate", "ipg", CCM_PCCR1, 28); + clk[IMX27_CLK_UART3_IPG_GATE] = imx_clk_gate("uart3_ipg_gate", "ipg", CCM_PCCR1, 29); + clk[IMX27_CLK_UART2_IPG_GATE] = imx_clk_gate("uart2_ipg_gate", "ipg", CCM_PCCR1, 30); + clk[IMX27_CLK_UART1_IPG_GATE] = imx_clk_gate("uart1_ipg_gate", "ipg", CCM_PCCR1, 31); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_register_clkdev(clk[IMX27_CLK_CPU_DIV], NULL, "cpu0"); + + clk_prepare_enable(clk[IMX27_CLK_EMI_AHB_GATE]); + + imx_register_uart_clocks(7); + + imx_print_silicon_rev("i.MX27", mx27_revision()); +} + +static void __init mx27_clocks_init_dt(struct device_node *np) +{ + struct device_node *refnp; + u32 fref = 26000000; /* default */ + + for_each_compatible_node(refnp, NULL, "fixed-clock") { + if (!of_device_is_compatible(refnp, "fsl,imx-osc26m")) + continue; + + if (!of_property_read_u32(refnp, "clock-frequency", &fref)) { + of_node_put(refnp); + break; + } + } + + ccm = of_iomap(np, 0); + + _mx27_clocks_init(fref); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(imx27_ccm, "fsl,imx27-ccm", mx27_clocks_init_dt); diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c new file mode 100644 index 000000000..c44e18c6f --- /dev/null +++ b/drivers/clk/imx/clk-imx31.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012 Sascha Hauer <kernel@pengutronix.de> + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <soc/imx/revision.h> +#include <soc/imx/timer.h> +#include <asm/irq.h> + +#include "clk.h" + +#define MX31_CCM_BASE_ADDR 0x53f80000 +#define MX31_GPT1_BASE_ADDR 0x53f90000 +#define MX31_INT_GPT (NR_IRQS_LEGACY + 29) + +#define MXC_CCM_CCMR 0x00 +#define MXC_CCM_PDR0 0x04 +#define MXC_CCM_PDR1 0x08 +#define MXC_CCM_MPCTL 0x10 +#define MXC_CCM_UPCTL 0x14 +#define MXC_CCM_SRPCTL 0x18 +#define MXC_CCM_CGR0 0x20 +#define MXC_CCM_CGR1 0x24 +#define MXC_CCM_CGR2 0x28 +#define MXC_CCM_PMCR0 0x5c + +static const char *mcu_main_sel[] = { "spll", "mpll", }; +static const char *per_sel[] = { "per_div", "ipg", }; +static const char *csi_sel[] = { "upll", "spll", }; +static const char *fir_sel[] = { "mcu_main", "upll", "spll" }; + +enum mx31_clks { + dummy, ckih, ckil, mpll, spll, upll, mcu_main, hsp, ahb, nfc, ipg, + per_div, per, csi, fir, csi_div, usb_div_pre, usb_div_post, fir_div_pre, + fir_div_post, sdhc1_gate, sdhc2_gate, gpt_gate, epit1_gate, epit2_gate, + iim_gate, ata_gate, sdma_gate, cspi3_gate, rng_gate, uart1_gate, + uart2_gate, ssi1_gate, i2c1_gate, i2c2_gate, i2c3_gate, hantro_gate, + mstick1_gate, mstick2_gate, csi_gate, rtc_gate, wdog_gate, pwm_gate, + sim_gate, ect_gate, usb_gate, kpp_gate, ipu_gate, uart3_gate, + uart4_gate, uart5_gate, owire_gate, ssi2_gate, cspi1_gate, cspi2_gate, + gacc_gate, emi_gate, rtic_gate, firi_gate, clk_max +}; + +static struct clk *clk[clk_max]; +static struct clk_onecell_data clk_data; + +static void __init _mx31_clocks_init(void __iomem *base, unsigned long fref) +{ + clk[dummy] = imx_clk_fixed("dummy", 0); + clk[ckih] = imx_clk_fixed("ckih", fref); + clk[ckil] = imx_clk_fixed("ckil", 32768); + clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX31, "mpll", "ckih", base + MXC_CCM_MPCTL); + clk[spll] = imx_clk_pllv1(IMX_PLLV1_IMX31, "spll", "ckih", base + MXC_CCM_SRPCTL); + clk[upll] = imx_clk_pllv1(IMX_PLLV1_IMX31, "upll", "ckih", base + MXC_CCM_UPCTL); + clk[mcu_main] = imx_clk_mux("mcu_main", base + MXC_CCM_PMCR0, 31, 1, mcu_main_sel, ARRAY_SIZE(mcu_main_sel)); + clk[hsp] = imx_clk_divider("hsp", "mcu_main", base + MXC_CCM_PDR0, 11, 3); + clk[ahb] = imx_clk_divider("ahb", "mcu_main", base + MXC_CCM_PDR0, 3, 3); + clk[nfc] = imx_clk_divider("nfc", "ahb", base + MXC_CCM_PDR0, 8, 3); + clk[ipg] = imx_clk_divider("ipg", "ahb", base + MXC_CCM_PDR0, 6, 2); + clk[per_div] = imx_clk_divider("per_div", "upll", base + MXC_CCM_PDR0, 16, 5); + clk[per] = imx_clk_mux("per", base + MXC_CCM_CCMR, 24, 1, per_sel, ARRAY_SIZE(per_sel)); + clk[csi] = imx_clk_mux("csi_sel", base + MXC_CCM_CCMR, 25, 1, csi_sel, ARRAY_SIZE(csi_sel)); + clk[fir] = imx_clk_mux("fir_sel", base + MXC_CCM_CCMR, 11, 2, fir_sel, ARRAY_SIZE(fir_sel)); + clk[csi_div] = imx_clk_divider("csi_div", "csi_sel", base + MXC_CCM_PDR0, 23, 9); + clk[usb_div_pre] = imx_clk_divider("usb_div_pre", "upll", base + MXC_CCM_PDR1, 30, 2); + clk[usb_div_post] = imx_clk_divider("usb_div_post", "usb_div_pre", base + MXC_CCM_PDR1, 27, 3); + clk[fir_div_pre] = imx_clk_divider("fir_div_pre", "fir_sel", base + MXC_CCM_PDR1, 24, 3); + clk[fir_div_post] = imx_clk_divider("fir_div_post", "fir_div_pre", base + MXC_CCM_PDR1, 23, 6); + clk[sdhc1_gate] = imx_clk_gate2("sdhc1_gate", "per", base + MXC_CCM_CGR0, 0); + clk[sdhc2_gate] = imx_clk_gate2("sdhc2_gate", "per", base + MXC_CCM_CGR0, 2); + clk[gpt_gate] = imx_clk_gate2("gpt_gate", "per", base + MXC_CCM_CGR0, 4); + clk[epit1_gate] = imx_clk_gate2("epit1_gate", "per", base + MXC_CCM_CGR0, 6); + clk[epit2_gate] = imx_clk_gate2("epit2_gate", "per", base + MXC_CCM_CGR0, 8); + clk[iim_gate] = imx_clk_gate2("iim_gate", "ipg", base + MXC_CCM_CGR0, 10); + clk[ata_gate] = imx_clk_gate2("ata_gate", "ipg", base + MXC_CCM_CGR0, 12); + clk[sdma_gate] = imx_clk_gate2("sdma_gate", "ahb", base + MXC_CCM_CGR0, 14); + clk[cspi3_gate] = imx_clk_gate2("cspi3_gate", "ipg", base + MXC_CCM_CGR0, 16); + clk[rng_gate] = imx_clk_gate2("rng_gate", "ipg", base + MXC_CCM_CGR0, 18); + clk[uart1_gate] = imx_clk_gate2("uart1_gate", "per", base + MXC_CCM_CGR0, 20); + clk[uart2_gate] = imx_clk_gate2("uart2_gate", "per", base + MXC_CCM_CGR0, 22); + clk[ssi1_gate] = imx_clk_gate2("ssi1_gate", "spll", base + MXC_CCM_CGR0, 24); + clk[i2c1_gate] = imx_clk_gate2("i2c1_gate", "per", base + MXC_CCM_CGR0, 26); + clk[i2c2_gate] = imx_clk_gate2("i2c2_gate", "per", base + MXC_CCM_CGR0, 28); + clk[i2c3_gate] = imx_clk_gate2("i2c3_gate", "per", base + MXC_CCM_CGR0, 30); + clk[hantro_gate] = imx_clk_gate2("hantro_gate", "per", base + MXC_CCM_CGR1, 0); + clk[mstick1_gate] = imx_clk_gate2("mstick1_gate", "per", base + MXC_CCM_CGR1, 2); + clk[mstick2_gate] = imx_clk_gate2("mstick2_gate", "per", base + MXC_CCM_CGR1, 4); + clk[csi_gate] = imx_clk_gate2("csi_gate", "csi_div", base + MXC_CCM_CGR1, 6); + clk[rtc_gate] = imx_clk_gate2("rtc_gate", "ipg", base + MXC_CCM_CGR1, 8); + clk[wdog_gate] = imx_clk_gate2("wdog_gate", "ipg", base + MXC_CCM_CGR1, 10); + clk[pwm_gate] = imx_clk_gate2("pwm_gate", "per", base + MXC_CCM_CGR1, 12); + clk[sim_gate] = imx_clk_gate2("sim_gate", "per", base + MXC_CCM_CGR1, 14); + clk[ect_gate] = imx_clk_gate2("ect_gate", "per", base + MXC_CCM_CGR1, 16); + clk[usb_gate] = imx_clk_gate2("usb_gate", "ahb", base + MXC_CCM_CGR1, 18); + clk[kpp_gate] = imx_clk_gate2("kpp_gate", "ipg", base + MXC_CCM_CGR1, 20); + clk[ipu_gate] = imx_clk_gate2("ipu_gate", "hsp", base + MXC_CCM_CGR1, 22); + clk[uart3_gate] = imx_clk_gate2("uart3_gate", "per", base + MXC_CCM_CGR1, 24); + clk[uart4_gate] = imx_clk_gate2("uart4_gate", "per", base + MXC_CCM_CGR1, 26); + clk[uart5_gate] = imx_clk_gate2("uart5_gate", "per", base + MXC_CCM_CGR1, 28); + clk[owire_gate] = imx_clk_gate2("owire_gate", "per", base + MXC_CCM_CGR1, 30); + clk[ssi2_gate] = imx_clk_gate2("ssi2_gate", "spll", base + MXC_CCM_CGR2, 0); + clk[cspi1_gate] = imx_clk_gate2("cspi1_gate", "ipg", base + MXC_CCM_CGR2, 2); + clk[cspi2_gate] = imx_clk_gate2("cspi2_gate", "ipg", base + MXC_CCM_CGR2, 4); + clk[gacc_gate] = imx_clk_gate2("gacc_gate", "per", base + MXC_CCM_CGR2, 6); + clk[emi_gate] = imx_clk_gate2("emi_gate", "ahb", base + MXC_CCM_CGR2, 8); + clk[rtic_gate] = imx_clk_gate2("rtic_gate", "ahb", base + MXC_CCM_CGR2, 10); + clk[firi_gate] = imx_clk_gate2("firi_gate", "upll", base+MXC_CCM_CGR2, 12); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_set_parent(clk[csi], clk[upll]); + clk_prepare_enable(clk[emi_gate]); + clk_prepare_enable(clk[iim_gate]); + mx31_revision(); + clk_disable_unprepare(clk[iim_gate]); +} + +static void __init mx31_clocks_init_dt(struct device_node *np) +{ + struct device_node *osc_np; + u32 fref = 26000000; /* default */ + void __iomem *ccm; + + for_each_compatible_node(osc_np, NULL, "fixed-clock") { + if (!of_device_is_compatible(osc_np, "fsl,imx-osc26m")) + continue; + + if (!of_property_read_u32(osc_np, "clock-frequency", &fref)) { + of_node_put(osc_np); + break; + } + } + + ccm = of_iomap(np, 0); + if (!ccm) + panic("%s: failed to map registers\n", __func__); + + _mx31_clocks_init(ccm, fref); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} + +CLK_OF_DECLARE(imx31_ccm, "fsl,imx31-ccm", mx31_clocks_init_dt); diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c new file mode 100644 index 000000000..0fe5ac210 --- /dev/null +++ b/drivers/clk/imx/clk-imx35.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2012 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + */ +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <linux/of.h> +#include <linux/err.h> +#include <soc/imx/revision.h> +#include <soc/imx/timer.h> +#include <asm/irq.h> + +#include "clk.h" + +#define MX35_CCM_BASE_ADDR 0x53f80000 +#define MX35_GPT1_BASE_ADDR 0x53f90000 +#define MX35_INT_GPT (NR_IRQS_LEGACY + 29) + +#define MXC_CCM_PDR0 0x04 +#define MX35_CCM_PDR2 0x0c +#define MX35_CCM_PDR3 0x10 +#define MX35_CCM_PDR4 0x14 +#define MX35_CCM_MPCTL 0x1c +#define MX35_CCM_PPCTL 0x20 +#define MX35_CCM_CGR0 0x2c +#define MX35_CCM_CGR1 0x30 +#define MX35_CCM_CGR2 0x34 +#define MX35_CCM_CGR3 0x38 + +struct arm_ahb_div { + unsigned char arm, ahb, sel; +}; + +static struct arm_ahb_div clk_consumer[] = { + { .arm = 1, .ahb = 4, .sel = 0}, + { .arm = 1, .ahb = 3, .sel = 1}, + { .arm = 2, .ahb = 2, .sel = 0}, + { .arm = 0, .ahb = 0, .sel = 0}, + { .arm = 0, .ahb = 0, .sel = 0}, + { .arm = 0, .ahb = 0, .sel = 0}, + { .arm = 4, .ahb = 1, .sel = 0}, + { .arm = 1, .ahb = 5, .sel = 0}, + { .arm = 1, .ahb = 8, .sel = 0}, + { .arm = 1, .ahb = 6, .sel = 1}, + { .arm = 2, .ahb = 4, .sel = 0}, + { .arm = 0, .ahb = 0, .sel = 0}, + { .arm = 0, .ahb = 0, .sel = 0}, + { .arm = 0, .ahb = 0, .sel = 0}, + { .arm = 4, .ahb = 2, .sel = 0}, + { .arm = 0, .ahb = 0, .sel = 0}, +}; + +static char hsp_div_532[] = { 4, 8, 3, 0 }; +static char hsp_div_400[] = { 3, 6, 3, 0 }; + +static struct clk_onecell_data clk_data; + +static const char *std_sel[] = {"ppll", "arm"}; +static const char *ipg_per_sel[] = {"ahb_per_div", "arm_per_div"}; + +enum mx35_clks { + /* 0 */ ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, + /* 9 */ ipg, arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div, + /* 15 */ esdhc_sel, esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel, + /* 20 */ spdif_div_pre, spdif_div_post, ssi_sel, ssi1_div_pre, + /* 24 */ ssi1_div_post, ssi2_div_pre, ssi2_div_post, usb_sel, usb_div, + /* 29 */ nfc_div, asrc_gate, pata_gate, audmux_gate, can1_gate, + /* 34 */ can2_gate, cspi1_gate, cspi2_gate, ect_gate, edio_gate, + /* 39 */ emi_gate, epit1_gate, epit2_gate, esai_gate, esdhc1_gate, + /* 44 */ esdhc2_gate, esdhc3_gate, fec_gate, gpio1_gate, gpio2_gate, + /* 49 */ gpio3_gate, gpt_gate, i2c1_gate, i2c2_gate, i2c3_gate, + /* 54 */ iomuxc_gate, ipu_gate, kpp_gate, mlb_gate, mshc_gate, + /* 59 */ owire_gate, pwm_gate, rngc_gate, rtc_gate, rtic_gate, scc_gate, + /* 65 */ sdma_gate, spba_gate, spdif_gate, ssi1_gate, ssi2_gate, + /* 70 */ uart1_gate, uart2_gate, uart3_gate, usbotg_gate, wdog_gate, + /* 75 */ max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate, + /* 81 */ gpu2d_gate, ckil, clk_max +}; + +static struct clk *clk[clk_max]; + +static void __init _mx35_clocks_init(void) +{ + void __iomem *base; + u32 pdr0, consumer_sel, hsp_sel; + struct arm_ahb_div *aad; + unsigned char *hsp_div; + + base = ioremap(MX35_CCM_BASE_ADDR, SZ_4K); + BUG_ON(!base); + + pdr0 = __raw_readl(base + MXC_CCM_PDR0); + consumer_sel = (pdr0 >> 16) & 0xf; + aad = &clk_consumer[consumer_sel]; + if (!aad->arm) { + pr_err("i.MX35 clk: illegal consumer mux selection 0x%x\n", consumer_sel); + /* + * We are basically stuck. Continue with a default entry and hope we + * get far enough to actually show the above message + */ + aad = &clk_consumer[0]; + } + + clk[ckih] = imx_clk_fixed("ckih", 24000000); + clk[ckil] = imx_clk_fixed("ckil", 32768); + clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "mpll", "ckih", base + MX35_CCM_MPCTL); + clk[ppll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "ppll", "ckih", base + MX35_CCM_PPCTL); + + clk[mpll] = imx_clk_fixed_factor("mpll_075", "mpll", 3, 4); + + if (aad->sel) + clk[arm] = imx_clk_fixed_factor("arm", "mpll_075", 1, aad->arm); + else + clk[arm] = imx_clk_fixed_factor("arm", "mpll", 1, aad->arm); + + if (clk_get_rate(clk[arm]) > 400000000) + hsp_div = hsp_div_532; + else + hsp_div = hsp_div_400; + + hsp_sel = (pdr0 >> 20) & 0x3; + if (!hsp_div[hsp_sel]) { + pr_err("i.MX35 clk: illegal hsp clk selection 0x%x\n", hsp_sel); + hsp_sel = 0; + } + + clk[hsp] = imx_clk_fixed_factor("hsp", "arm", 1, hsp_div[hsp_sel]); + + clk[ahb] = imx_clk_fixed_factor("ahb", "arm", 1, aad->ahb); + clk[ipg] = imx_clk_fixed_factor("ipg", "ahb", 1, 2); + + clk[arm_per_div] = imx_clk_divider("arm_per_div", "arm", base + MX35_CCM_PDR4, 16, 6); + clk[ahb_per_div] = imx_clk_divider("ahb_per_div", "ahb", base + MXC_CCM_PDR0, 12, 3); + clk[ipg_per] = imx_clk_mux("ipg_per", base + MXC_CCM_PDR0, 26, 1, ipg_per_sel, ARRAY_SIZE(ipg_per_sel)); + + clk[uart_sel] = imx_clk_mux("uart_sel", base + MX35_CCM_PDR3, 14, 1, std_sel, ARRAY_SIZE(std_sel)); + clk[uart_div] = imx_clk_divider("uart_div", "uart_sel", base + MX35_CCM_PDR4, 10, 6); + + clk[esdhc_sel] = imx_clk_mux("esdhc_sel", base + MX35_CCM_PDR4, 9, 1, std_sel, ARRAY_SIZE(std_sel)); + clk[esdhc1_div] = imx_clk_divider("esdhc1_div", "esdhc_sel", base + MX35_CCM_PDR3, 0, 6); + clk[esdhc2_div] = imx_clk_divider("esdhc2_div", "esdhc_sel", base + MX35_CCM_PDR3, 8, 6); + clk[esdhc3_div] = imx_clk_divider("esdhc3_div", "esdhc_sel", base + MX35_CCM_PDR3, 16, 6); + + clk[spdif_sel] = imx_clk_mux("spdif_sel", base + MX35_CCM_PDR3, 22, 1, std_sel, ARRAY_SIZE(std_sel)); + clk[spdif_div_pre] = imx_clk_divider("spdif_div_pre", "spdif_sel", base + MX35_CCM_PDR3, 29, 3); /* divide by 1 not allowed */ + clk[spdif_div_post] = imx_clk_divider("spdif_div_post", "spdif_div_pre", base + MX35_CCM_PDR3, 23, 6); + + clk[ssi_sel] = imx_clk_mux("ssi_sel", base + MX35_CCM_PDR2, 6, 1, std_sel, ARRAY_SIZE(std_sel)); + clk[ssi1_div_pre] = imx_clk_divider("ssi1_div_pre", "ssi_sel", base + MX35_CCM_PDR2, 24, 3); + clk[ssi1_div_post] = imx_clk_divider("ssi1_div_post", "ssi1_div_pre", base + MX35_CCM_PDR2, 0, 6); + clk[ssi2_div_pre] = imx_clk_divider("ssi2_div_pre", "ssi_sel", base + MX35_CCM_PDR2, 27, 3); + clk[ssi2_div_post] = imx_clk_divider("ssi2_div_post", "ssi2_div_pre", base + MX35_CCM_PDR2, 8, 6); + + clk[usb_sel] = imx_clk_mux("usb_sel", base + MX35_CCM_PDR4, 9, 1, std_sel, ARRAY_SIZE(std_sel)); + clk[usb_div] = imx_clk_divider("usb_div", "usb_sel", base + MX35_CCM_PDR4, 22, 6); + + clk[nfc_div] = imx_clk_divider("nfc_div", "ahb", base + MX35_CCM_PDR4, 28, 4); + + clk[csi_sel] = imx_clk_mux("csi_sel", base + MX35_CCM_PDR2, 7, 1, std_sel, ARRAY_SIZE(std_sel)); + clk[csi_div] = imx_clk_divider("csi_div", "csi_sel", base + MX35_CCM_PDR2, 16, 6); + + clk[asrc_gate] = imx_clk_gate2("asrc_gate", "ipg", base + MX35_CCM_CGR0, 0); + clk[pata_gate] = imx_clk_gate2("pata_gate", "ipg", base + MX35_CCM_CGR0, 2); + clk[audmux_gate] = imx_clk_gate2("audmux_gate", "ipg", base + MX35_CCM_CGR0, 4); + clk[can1_gate] = imx_clk_gate2("can1_gate", "ipg", base + MX35_CCM_CGR0, 6); + clk[can2_gate] = imx_clk_gate2("can2_gate", "ipg", base + MX35_CCM_CGR0, 8); + clk[cspi1_gate] = imx_clk_gate2("cspi1_gate", "ipg", base + MX35_CCM_CGR0, 10); + clk[cspi2_gate] = imx_clk_gate2("cspi2_gate", "ipg", base + MX35_CCM_CGR0, 12); + clk[ect_gate] = imx_clk_gate2("ect_gate", "ipg", base + MX35_CCM_CGR0, 14); + clk[edio_gate] = imx_clk_gate2("edio_gate", "ipg", base + MX35_CCM_CGR0, 16); + clk[emi_gate] = imx_clk_gate2("emi_gate", "ipg", base + MX35_CCM_CGR0, 18); + clk[epit1_gate] = imx_clk_gate2("epit1_gate", "ipg", base + MX35_CCM_CGR0, 20); + clk[epit2_gate] = imx_clk_gate2("epit2_gate", "ipg", base + MX35_CCM_CGR0, 22); + clk[esai_gate] = imx_clk_gate2("esai_gate", "ipg", base + MX35_CCM_CGR0, 24); + clk[esdhc1_gate] = imx_clk_gate2("esdhc1_gate", "esdhc1_div", base + MX35_CCM_CGR0, 26); + clk[esdhc2_gate] = imx_clk_gate2("esdhc2_gate", "esdhc2_div", base + MX35_CCM_CGR0, 28); + clk[esdhc3_gate] = imx_clk_gate2("esdhc3_gate", "esdhc3_div", base + MX35_CCM_CGR0, 30); + + clk[fec_gate] = imx_clk_gate2("fec_gate", "ipg", base + MX35_CCM_CGR1, 0); + clk[gpio1_gate] = imx_clk_gate2("gpio1_gate", "ipg", base + MX35_CCM_CGR1, 2); + clk[gpio2_gate] = imx_clk_gate2("gpio2_gate", "ipg", base + MX35_CCM_CGR1, 4); + clk[gpio3_gate] = imx_clk_gate2("gpio3_gate", "ipg", base + MX35_CCM_CGR1, 6); + clk[gpt_gate] = imx_clk_gate2("gpt_gate", "ipg", base + MX35_CCM_CGR1, 8); + clk[i2c1_gate] = imx_clk_gate2("i2c1_gate", "ipg_per", base + MX35_CCM_CGR1, 10); + clk[i2c2_gate] = imx_clk_gate2("i2c2_gate", "ipg_per", base + MX35_CCM_CGR1, 12); + clk[i2c3_gate] = imx_clk_gate2("i2c3_gate", "ipg_per", base + MX35_CCM_CGR1, 14); + clk[iomuxc_gate] = imx_clk_gate2("iomuxc_gate", "ipg", base + MX35_CCM_CGR1, 16); + clk[ipu_gate] = imx_clk_gate2("ipu_gate", "hsp", base + MX35_CCM_CGR1, 18); + clk[kpp_gate] = imx_clk_gate2("kpp_gate", "ipg", base + MX35_CCM_CGR1, 20); + clk[mlb_gate] = imx_clk_gate2("mlb_gate", "ahb", base + MX35_CCM_CGR1, 22); + clk[mshc_gate] = imx_clk_gate2("mshc_gate", "dummy", base + MX35_CCM_CGR1, 24); + clk[owire_gate] = imx_clk_gate2("owire_gate", "ipg_per", base + MX35_CCM_CGR1, 26); + clk[pwm_gate] = imx_clk_gate2("pwm_gate", "ipg_per", base + MX35_CCM_CGR1, 28); + clk[rngc_gate] = imx_clk_gate2("rngc_gate", "ipg", base + MX35_CCM_CGR1, 30); + + clk[rtc_gate] = imx_clk_gate2("rtc_gate", "ipg", base + MX35_CCM_CGR2, 0); + clk[rtic_gate] = imx_clk_gate2("rtic_gate", "ahb", base + MX35_CCM_CGR2, 2); + clk[scc_gate] = imx_clk_gate2("scc_gate", "ipg", base + MX35_CCM_CGR2, 4); + clk[sdma_gate] = imx_clk_gate2("sdma_gate", "ahb", base + MX35_CCM_CGR2, 6); + clk[spba_gate] = imx_clk_gate2("spba_gate", "ipg", base + MX35_CCM_CGR2, 8); + clk[spdif_gate] = imx_clk_gate2("spdif_gate", "spdif_div_post", base + MX35_CCM_CGR2, 10); + clk[ssi1_gate] = imx_clk_gate2("ssi1_gate", "ssi1_div_post", base + MX35_CCM_CGR2, 12); + clk[ssi2_gate] = imx_clk_gate2("ssi2_gate", "ssi2_div_post", base + MX35_CCM_CGR2, 14); + clk[uart1_gate] = imx_clk_gate2("uart1_gate", "uart_div", base + MX35_CCM_CGR2, 16); + clk[uart2_gate] = imx_clk_gate2("uart2_gate", "uart_div", base + MX35_CCM_CGR2, 18); + clk[uart3_gate] = imx_clk_gate2("uart3_gate", "uart_div", base + MX35_CCM_CGR2, 20); + clk[usbotg_gate] = imx_clk_gate2("usbotg_gate", "ahb", base + MX35_CCM_CGR2, 22); + clk[wdog_gate] = imx_clk_gate2("wdog_gate", "ipg", base + MX35_CCM_CGR2, 24); + clk[max_gate] = imx_clk_gate2("max_gate", "dummy", base + MX35_CCM_CGR2, 26); + clk[admux_gate] = imx_clk_gate2("admux_gate", "ipg", base + MX35_CCM_CGR2, 30); + + clk[csi_gate] = imx_clk_gate2("csi_gate", "csi_div", base + MX35_CCM_CGR3, 0); + clk[iim_gate] = imx_clk_gate2("iim_gate", "ipg", base + MX35_CCM_CGR3, 2); + clk[gpu2d_gate] = imx_clk_gate2("gpu2d_gate", "ahb", base + MX35_CCM_CGR3, 4); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_prepare_enable(clk[spba_gate]); + clk_prepare_enable(clk[gpio1_gate]); + clk_prepare_enable(clk[gpio2_gate]); + clk_prepare_enable(clk[gpio3_gate]); + clk_prepare_enable(clk[iim_gate]); + clk_prepare_enable(clk[emi_gate]); + clk_prepare_enable(clk[max_gate]); + clk_prepare_enable(clk[iomuxc_gate]); + + /* + * SCC is needed to boot via mmc after a watchdog reset. The clock code + * before conversion to common clk also enabled UART1 (which isn't + * handled here and not needed for mmc) and IIM (which is enabled + * unconditionally above). + */ + clk_prepare_enable(clk[scc_gate]); + + imx_register_uart_clocks(4); + + imx_print_silicon_rev("i.MX35", mx35_revision()); +} + +static void __init mx35_clocks_init_dt(struct device_node *ccm_node) +{ + _mx35_clocks_init(); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(ccm_node, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(imx35, "fsl,imx35-ccm", mx35_clocks_init_dt); diff --git a/drivers/clk/imx/clk-imx5.c b/drivers/clk/imx/clk-imx5.c new file mode 100644 index 000000000..e44938464 --- /dev/null +++ b/drivers/clk/imx/clk-imx5.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + */ +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/sizes.h> +#include <soc/imx/revision.h> +#include <dt-bindings/clock/imx5-clock.h> + +#include "clk.h" + +#define MX51_DPLL1_BASE 0x83f80000 +#define MX51_DPLL2_BASE 0x83f84000 +#define MX51_DPLL3_BASE 0x83f88000 + +#define MX53_DPLL1_BASE 0x63f80000 +#define MX53_DPLL2_BASE 0x63f84000 +#define MX53_DPLL3_BASE 0x63f88000 +#define MX53_DPLL4_BASE 0x63f8c000 + +#define MXC_CCM_CCR (ccm_base + 0x00) +#define MXC_CCM_CCDR (ccm_base + 0x04) +#define MXC_CCM_CSR (ccm_base + 0x08) +#define MXC_CCM_CCSR (ccm_base + 0x0c) +#define MXC_CCM_CACRR (ccm_base + 0x10) +#define MXC_CCM_CBCDR (ccm_base + 0x14) +#define MXC_CCM_CBCMR (ccm_base + 0x18) +#define MXC_CCM_CSCMR1 (ccm_base + 0x1c) +#define MXC_CCM_CSCMR2 (ccm_base + 0x20) +#define MXC_CCM_CSCDR1 (ccm_base + 0x24) +#define MXC_CCM_CS1CDR (ccm_base + 0x28) +#define MXC_CCM_CS2CDR (ccm_base + 0x2c) +#define MXC_CCM_CDCDR (ccm_base + 0x30) +#define MXC_CCM_CHSCDR (ccm_base + 0x34) +#define MXC_CCM_CSCDR2 (ccm_base + 0x38) +#define MXC_CCM_CSCDR3 (ccm_base + 0x3c) +#define MXC_CCM_CSCDR4 (ccm_base + 0x40) +#define MXC_CCM_CWDR (ccm_base + 0x44) +#define MXC_CCM_CDHIPR (ccm_base + 0x48) +#define MXC_CCM_CDCR (ccm_base + 0x4c) +#define MXC_CCM_CTOR (ccm_base + 0x50) +#define MXC_CCM_CLPCR (ccm_base + 0x54) +#define MXC_CCM_CISR (ccm_base + 0x58) +#define MXC_CCM_CIMR (ccm_base + 0x5c) +#define MXC_CCM_CCOSR (ccm_base + 0x60) +#define MXC_CCM_CGPR (ccm_base + 0x64) +#define MXC_CCM_CCGR0 (ccm_base + 0x68) +#define MXC_CCM_CCGR1 (ccm_base + 0x6c) +#define MXC_CCM_CCGR2 (ccm_base + 0x70) +#define MXC_CCM_CCGR3 (ccm_base + 0x74) +#define MXC_CCM_CCGR4 (ccm_base + 0x78) +#define MXC_CCM_CCGR5 (ccm_base + 0x7c) +#define MXC_CCM_CCGR6 (ccm_base + 0x80) +#define MXC_CCM_CCGR7 (ccm_base + 0x84) + +/* Low-power Audio Playback Mode clock */ +static const char *lp_apm_sel[] = { "osc", }; + +/* This is used multiple times */ +static const char *standard_pll_sel[] = { "pll1_sw", "pll2_sw", "pll3_sw", "lp_apm", }; +static const char *periph_apm_sel[] = { "pll1_sw", "pll3_sw", "lp_apm", }; +static const char *main_bus_sel[] = { "pll2_sw", "periph_apm", }; +static const char *per_lp_apm_sel[] = { "main_bus", "lp_apm", }; +static const char *per_root_sel[] = { "per_podf", "ipg", }; +static const char *esdhc_c_sel[] = { "esdhc_a_podf", "esdhc_b_podf", }; +static const char *esdhc_d_sel[] = { "esdhc_a_podf", "esdhc_b_podf", }; +static const char *ssi_apm_sels[] = { "ckih1", "lp_amp", "ckih2", }; +static const char *ssi_clk_sels[] = { "pll1_sw", "pll2_sw", "pll3_sw", "ssi_apm", }; +static const char *ssi3_clk_sels[] = { "ssi1_root_gate", "ssi2_root_gate", }; +static const char *ssi_ext1_com_sels[] = { "ssi_ext1_podf", "ssi1_root_gate", }; +static const char *ssi_ext2_com_sels[] = { "ssi_ext2_podf", "ssi2_root_gate", }; +static const char *emi_slow_sel[] = { "main_bus", "ahb", }; +static const char *usb_phy_sel_str[] = { "osc", "usb_phy_podf", }; +static const char *mx51_ipu_di0_sel[] = { "di_pred", "osc", "ckih1", "tve_di", }; +static const char *mx53_ipu_di0_sel[] = { "di_pred", "osc", "ckih1", "di_pll4_podf", "dummy", "ldb_di0_gate", }; +static const char *mx53_ldb_di0_sel[] = { "pll3_sw", "pll4_sw", }; +static const char *mx51_ipu_di1_sel[] = { "di_pred", "osc", "ckih1", "tve_di", "ipp_di1", }; +static const char *mx53_ipu_di1_sel[] = { "di_pred", "osc", "ckih1", "tve_di", "ipp_di1", "ldb_di1_gate", }; +static const char *mx53_ldb_di1_sel[] = { "pll3_sw", "pll4_sw", }; +static const char *mx51_tve_ext_sel[] = { "osc", "ckih1", }; +static const char *mx53_tve_ext_sel[] = { "pll4_sw", "ckih1", }; +static const char *mx51_tve_sel[] = { "tve_pred", "tve_ext_sel", }; +static const char *ipu_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb", }; +static const char *gpu3d_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb" }; +static const char *gpu2d_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb" }; +static const char *vpu_sel[] = { "axi_a", "axi_b", "emi_slow_gate", "ahb", }; +static const char *mx53_can_sel[] = { "ipg", "ckih1", "ckih2", "lp_apm", }; +static const char *mx53_cko1_sel[] = { + "cpu_podf", "pll1_sw", "pll2_sw", "pll3_sw", + "emi_slow_podf", "pll4_sw", "nfc_podf", "dummy", + "di_pred", "dummy", "dummy", "ahb", + "ipg", "per_root", "ckil", "dummy",}; +static const char *mx53_cko2_sel[] = { + "dummy"/* dptc_core */, "dummy"/* dptc_perich */, + "dummy", "esdhc_a_podf", + "usboh3_podf", "dummy"/* wrck_clk_root */, + "ecspi_podf", "dummy"/* pll1_ref_clk */, + "esdhc_b_podf", "dummy"/* ddr_clk_root */, + "dummy"/* arm_axi_clk_root */, "dummy"/* usb_phy_out */, + "vpu_sel", "ipu_sel", + "osc", "ckih1", + "dummy", "esdhc_c_sel", + "ssi1_root_podf", "ssi2_root_podf", + "dummy", "dummy", + "dummy"/* lpsr_clk_root */, "dummy"/* pgc_clk_root */, + "dummy"/* tve_out */, "usb_phy_sel", + "tve_sel", "lp_apm", + "uart_root", "dummy"/* spdif0_clk_root */, + "dummy", "dummy", }; +static const char *mx51_spdif_xtal_sel[] = { "osc", "ckih", "ckih2", }; +static const char *mx53_spdif_xtal_sel[] = { "osc", "ckih", "ckih2", "pll4_sw", }; +static const char *spdif_sel[] = { "pll1_sw", "pll2_sw", "pll3_sw", "spdif_xtal_sel", }; +static const char *spdif0_com_sel[] = { "spdif0_podf", "ssi1_root_gate", }; +static const char *mx51_spdif1_com_sel[] = { "spdif1_podf", "ssi2_root_gate", }; +static const char *step_sels[] = { "lp_apm", }; +static const char *cpu_podf_sels[] = { "pll1_sw", "step_sel" }; +static const char *ieee1588_sels[] = { "pll3_sw", "pll4_sw", "dummy" /* usbphy2_clk */, "dummy" /* fec_phy_clk */ }; + +static struct clk *clk[IMX5_CLK_END]; +static struct clk_onecell_data clk_data; + +static void __init mx5_clocks_common_init(void __iomem *ccm_base) +{ + clk[IMX5_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[IMX5_CLK_CKIL] = imx_obtain_fixed_clock("ckil", 0); + clk[IMX5_CLK_OSC] = imx_obtain_fixed_clock("osc", 0); + clk[IMX5_CLK_CKIH1] = imx_obtain_fixed_clock("ckih1", 0); + clk[IMX5_CLK_CKIH2] = imx_obtain_fixed_clock("ckih2", 0); + + clk[IMX5_CLK_PER_LP_APM] = imx_clk_mux("per_lp_apm", MXC_CCM_CBCMR, 1, 1, + per_lp_apm_sel, ARRAY_SIZE(per_lp_apm_sel)); + clk[IMX5_CLK_PER_PRED1] = imx_clk_divider("per_pred1", "per_lp_apm", MXC_CCM_CBCDR, 6, 2); + clk[IMX5_CLK_PER_PRED2] = imx_clk_divider("per_pred2", "per_pred1", MXC_CCM_CBCDR, 3, 3); + clk[IMX5_CLK_PER_PODF] = imx_clk_divider("per_podf", "per_pred2", MXC_CCM_CBCDR, 0, 3); + clk[IMX5_CLK_PER_ROOT] = imx_clk_mux("per_root", MXC_CCM_CBCMR, 0, 1, + per_root_sel, ARRAY_SIZE(per_root_sel)); + clk[IMX5_CLK_AHB] = imx_clk_divider("ahb", "main_bus", MXC_CCM_CBCDR, 10, 3); + clk[IMX5_CLK_AHB_MAX] = imx_clk_gate2_flags("ahb_max", "ahb", MXC_CCM_CCGR0, 28, CLK_IS_CRITICAL); + clk[IMX5_CLK_AIPS_TZ1] = imx_clk_gate2_flags("aips_tz1", "ahb", MXC_CCM_CCGR0, 24, CLK_IS_CRITICAL); + clk[IMX5_CLK_AIPS_TZ2] = imx_clk_gate2_flags("aips_tz2", "ahb", MXC_CCM_CCGR0, 26, CLK_IS_CRITICAL); + clk[IMX5_CLK_TMAX1] = imx_clk_gate2_flags("tmax1", "ahb", MXC_CCM_CCGR1, 0, CLK_IS_CRITICAL); + clk[IMX5_CLK_TMAX2] = imx_clk_gate2_flags("tmax2", "ahb", MXC_CCM_CCGR1, 2, CLK_IS_CRITICAL); + clk[IMX5_CLK_TMAX3] = imx_clk_gate2_flags("tmax3", "ahb", MXC_CCM_CCGR1, 4, CLK_IS_CRITICAL); + clk[IMX5_CLK_SPBA] = imx_clk_gate2_flags("spba", "ipg", MXC_CCM_CCGR5, 0, CLK_IS_CRITICAL); + clk[IMX5_CLK_IPG] = imx_clk_divider("ipg", "ahb", MXC_CCM_CBCDR, 8, 2); + clk[IMX5_CLK_AXI_A] = imx_clk_divider("axi_a", "main_bus", MXC_CCM_CBCDR, 16, 3); + clk[IMX5_CLK_AXI_B] = imx_clk_divider("axi_b", "main_bus", MXC_CCM_CBCDR, 19, 3); + clk[IMX5_CLK_UART_SEL] = imx_clk_mux("uart_sel", MXC_CCM_CSCMR1, 24, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_UART_PRED] = imx_clk_divider("uart_pred", "uart_sel", MXC_CCM_CSCDR1, 3, 3); + clk[IMX5_CLK_UART_ROOT] = imx_clk_divider("uart_root", "uart_pred", MXC_CCM_CSCDR1, 0, 3); + + clk[IMX5_CLK_ESDHC_A_PRED] = imx_clk_divider("esdhc_a_pred", "esdhc_a_sel", MXC_CCM_CSCDR1, 16, 3); + clk[IMX5_CLK_ESDHC_A_PODF] = imx_clk_divider("esdhc_a_podf", "esdhc_a_pred", MXC_CCM_CSCDR1, 11, 3); + clk[IMX5_CLK_ESDHC_B_PRED] = imx_clk_divider("esdhc_b_pred", "esdhc_b_sel", MXC_CCM_CSCDR1, 22, 3); + clk[IMX5_CLK_ESDHC_B_PODF] = imx_clk_divider("esdhc_b_podf", "esdhc_b_pred", MXC_CCM_CSCDR1, 19, 3); + + clk[IMX5_CLK_EMI_SEL] = imx_clk_mux("emi_sel", MXC_CCM_CBCDR, 26, 1, + emi_slow_sel, ARRAY_SIZE(emi_slow_sel)); + clk[IMX5_CLK_EMI_SLOW_PODF] = imx_clk_divider("emi_slow_podf", "emi_sel", MXC_CCM_CBCDR, 22, 3); + clk[IMX5_CLK_NFC_PODF] = imx_clk_divider("nfc_podf", "emi_slow_podf", MXC_CCM_CBCDR, 13, 3); + clk[IMX5_CLK_ECSPI_SEL] = imx_clk_mux("ecspi_sel", MXC_CCM_CSCMR1, 4, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_ECSPI_PRED] = imx_clk_divider("ecspi_pred", "ecspi_sel", MXC_CCM_CSCDR2, 25, 3); + clk[IMX5_CLK_ECSPI_PODF] = imx_clk_divider("ecspi_podf", "ecspi_pred", MXC_CCM_CSCDR2, 19, 6); + clk[IMX5_CLK_USBOH3_SEL] = imx_clk_mux("usboh3_sel", MXC_CCM_CSCMR1, 22, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_USBOH3_PRED] = imx_clk_divider("usboh3_pred", "usboh3_sel", MXC_CCM_CSCDR1, 8, 3); + clk[IMX5_CLK_USBOH3_PODF] = imx_clk_divider("usboh3_podf", "usboh3_pred", MXC_CCM_CSCDR1, 6, 2); + clk[IMX5_CLK_USB_PHY_PRED] = imx_clk_divider("usb_phy_pred", "pll3_sw", MXC_CCM_CDCDR, 3, 3); + clk[IMX5_CLK_USB_PHY_PODF] = imx_clk_divider("usb_phy_podf", "usb_phy_pred", MXC_CCM_CDCDR, 0, 3); + clk[IMX5_CLK_USB_PHY_SEL] = imx_clk_mux("usb_phy_sel", MXC_CCM_CSCMR1, 26, 1, + usb_phy_sel_str, ARRAY_SIZE(usb_phy_sel_str)); + clk[IMX5_CLK_STEP_SEL] = imx_clk_mux("step_sel", MXC_CCM_CCSR, 7, 2, step_sels, ARRAY_SIZE(step_sels)); + clk[IMX5_CLK_CPU_PODF_SEL] = imx_clk_mux("cpu_podf_sel", MXC_CCM_CCSR, 2, 1, cpu_podf_sels, ARRAY_SIZE(cpu_podf_sels)); + clk[IMX5_CLK_CPU_PODF] = imx_clk_divider("cpu_podf", "cpu_podf_sel", MXC_CCM_CACRR, 0, 3); + clk[IMX5_CLK_DI_PRED] = imx_clk_divider("di_pred", "pll3_sw", MXC_CCM_CDCDR, 6, 3); + clk[IMX5_CLK_IIM_GATE] = imx_clk_gate2("iim_gate", "ipg", MXC_CCM_CCGR0, 30); + clk[IMX5_CLK_UART1_IPG_GATE] = imx_clk_gate2("uart1_ipg_gate", "ipg", MXC_CCM_CCGR1, 6); + clk[IMX5_CLK_UART1_PER_GATE] = imx_clk_gate2("uart1_per_gate", "uart_root", MXC_CCM_CCGR1, 8); + clk[IMX5_CLK_UART2_IPG_GATE] = imx_clk_gate2("uart2_ipg_gate", "ipg", MXC_CCM_CCGR1, 10); + clk[IMX5_CLK_UART2_PER_GATE] = imx_clk_gate2("uart2_per_gate", "uart_root", MXC_CCM_CCGR1, 12); + clk[IMX5_CLK_UART3_IPG_GATE] = imx_clk_gate2("uart3_ipg_gate", "ipg", MXC_CCM_CCGR1, 14); + clk[IMX5_CLK_UART3_PER_GATE] = imx_clk_gate2("uart3_per_gate", "uart_root", MXC_CCM_CCGR1, 16); + clk[IMX5_CLK_I2C1_GATE] = imx_clk_gate2("i2c1_gate", "per_root", MXC_CCM_CCGR1, 18); + clk[IMX5_CLK_I2C2_GATE] = imx_clk_gate2("i2c2_gate", "per_root", MXC_CCM_CCGR1, 20); + clk[IMX5_CLK_PWM1_IPG_GATE] = imx_clk_gate2("pwm1_ipg_gate", "ipg", MXC_CCM_CCGR2, 10); + clk[IMX5_CLK_PWM1_HF_GATE] = imx_clk_gate2("pwm1_hf_gate", "per_root", MXC_CCM_CCGR2, 12); + clk[IMX5_CLK_PWM2_IPG_GATE] = imx_clk_gate2("pwm2_ipg_gate", "ipg", MXC_CCM_CCGR2, 14); + clk[IMX5_CLK_PWM2_HF_GATE] = imx_clk_gate2("pwm2_hf_gate", "per_root", MXC_CCM_CCGR2, 16); + clk[IMX5_CLK_GPT_IPG_GATE] = imx_clk_gate2("gpt_ipg_gate", "ipg", MXC_CCM_CCGR2, 18); + clk[IMX5_CLK_GPT_HF_GATE] = imx_clk_gate2("gpt_hf_gate", "per_root", MXC_CCM_CCGR2, 20); + clk[IMX5_CLK_FEC_GATE] = imx_clk_gate2("fec_gate", "ipg", MXC_CCM_CCGR2, 24); + clk[IMX5_CLK_USBOH3_GATE] = imx_clk_gate2("usboh3_gate", "ipg", MXC_CCM_CCGR2, 26); + clk[IMX5_CLK_USBOH3_PER_GATE] = imx_clk_gate2("usboh3_per_gate", "usboh3_podf", MXC_CCM_CCGR2, 28); + clk[IMX5_CLK_ESDHC1_IPG_GATE] = imx_clk_gate2("esdhc1_ipg_gate", "ipg", MXC_CCM_CCGR3, 0); + clk[IMX5_CLK_ESDHC2_IPG_GATE] = imx_clk_gate2("esdhc2_ipg_gate", "ipg", MXC_CCM_CCGR3, 4); + clk[IMX5_CLK_ESDHC3_IPG_GATE] = imx_clk_gate2("esdhc3_ipg_gate", "ipg", MXC_CCM_CCGR3, 8); + clk[IMX5_CLK_ESDHC4_IPG_GATE] = imx_clk_gate2("esdhc4_ipg_gate", "ipg", MXC_CCM_CCGR3, 12); + clk[IMX5_CLK_SSI1_IPG_GATE] = imx_clk_gate2("ssi1_ipg_gate", "ipg", MXC_CCM_CCGR3, 16); + clk[IMX5_CLK_SSI2_IPG_GATE] = imx_clk_gate2("ssi2_ipg_gate", "ipg", MXC_CCM_CCGR3, 20); + clk[IMX5_CLK_SSI3_IPG_GATE] = imx_clk_gate2("ssi3_ipg_gate", "ipg", MXC_CCM_CCGR3, 24); + clk[IMX5_CLK_ECSPI1_IPG_GATE] = imx_clk_gate2("ecspi1_ipg_gate", "ipg", MXC_CCM_CCGR4, 18); + clk[IMX5_CLK_ECSPI1_PER_GATE] = imx_clk_gate2("ecspi1_per_gate", "ecspi_podf", MXC_CCM_CCGR4, 20); + clk[IMX5_CLK_ECSPI2_IPG_GATE] = imx_clk_gate2("ecspi2_ipg_gate", "ipg", MXC_CCM_CCGR4, 22); + clk[IMX5_CLK_ECSPI2_PER_GATE] = imx_clk_gate2("ecspi2_per_gate", "ecspi_podf", MXC_CCM_CCGR4, 24); + clk[IMX5_CLK_CSPI_IPG_GATE] = imx_clk_gate2("cspi_ipg_gate", "ipg", MXC_CCM_CCGR4, 26); + clk[IMX5_CLK_SDMA_GATE] = imx_clk_gate2("sdma_gate", "ipg", MXC_CCM_CCGR4, 30); + clk[IMX5_CLK_EMI_FAST_GATE] = imx_clk_gate2_flags("emi_fast_gate", "dummy", MXC_CCM_CCGR5, 14, CLK_IS_CRITICAL); + clk[IMX5_CLK_EMI_SLOW_GATE] = imx_clk_gate2_flags("emi_slow_gate", "emi_slow_podf", MXC_CCM_CCGR5, 16, CLK_IS_CRITICAL); + clk[IMX5_CLK_IPU_SEL] = imx_clk_mux("ipu_sel", MXC_CCM_CBCMR, 6, 2, ipu_sel, ARRAY_SIZE(ipu_sel)); + clk[IMX5_CLK_IPU_GATE] = imx_clk_gate2("ipu_gate", "ipu_sel", MXC_CCM_CCGR5, 10); + clk[IMX5_CLK_NFC_GATE] = imx_clk_gate2("nfc_gate", "nfc_podf", MXC_CCM_CCGR5, 20); + clk[IMX5_CLK_IPU_DI0_GATE] = imx_clk_gate2("ipu_di0_gate", "ipu_di0_sel", MXC_CCM_CCGR6, 10); + clk[IMX5_CLK_IPU_DI1_GATE] = imx_clk_gate2("ipu_di1_gate", "ipu_di1_sel", MXC_CCM_CCGR6, 12); + clk[IMX5_CLK_GPU3D_SEL] = imx_clk_mux("gpu3d_sel", MXC_CCM_CBCMR, 4, 2, gpu3d_sel, ARRAY_SIZE(gpu3d_sel)); + clk[IMX5_CLK_GPU2D_SEL] = imx_clk_mux("gpu2d_sel", MXC_CCM_CBCMR, 16, 2, gpu2d_sel, ARRAY_SIZE(gpu2d_sel)); + clk[IMX5_CLK_GPU3D_GATE] = imx_clk_gate2("gpu3d_gate", "gpu3d_sel", MXC_CCM_CCGR5, 2); + clk[IMX5_CLK_GARB_GATE] = imx_clk_gate2("garb_gate", "axi_a", MXC_CCM_CCGR5, 4); + clk[IMX5_CLK_GPU2D_GATE] = imx_clk_gate2("gpu2d_gate", "gpu2d_sel", MXC_CCM_CCGR6, 14); + clk[IMX5_CLK_VPU_SEL] = imx_clk_mux("vpu_sel", MXC_CCM_CBCMR, 14, 2, vpu_sel, ARRAY_SIZE(vpu_sel)); + clk[IMX5_CLK_VPU_GATE] = imx_clk_gate2("vpu_gate", "vpu_sel", MXC_CCM_CCGR5, 6); + clk[IMX5_CLK_VPU_REFERENCE_GATE] = imx_clk_gate2("vpu_reference_gate", "osc", MXC_CCM_CCGR5, 8); + clk[IMX5_CLK_GPC_DVFS] = imx_clk_gate2_flags("gpc_dvfs", "dummy", MXC_CCM_CCGR5, 24, CLK_IS_CRITICAL); + + clk[IMX5_CLK_SSI_APM] = imx_clk_mux("ssi_apm", MXC_CCM_CSCMR1, 8, 2, ssi_apm_sels, ARRAY_SIZE(ssi_apm_sels)); + clk[IMX5_CLK_SSI1_ROOT_SEL] = imx_clk_mux("ssi1_root_sel", MXC_CCM_CSCMR1, 14, 2, ssi_clk_sels, ARRAY_SIZE(ssi_clk_sels)); + clk[IMX5_CLK_SSI2_ROOT_SEL] = imx_clk_mux("ssi2_root_sel", MXC_CCM_CSCMR1, 12, 2, ssi_clk_sels, ARRAY_SIZE(ssi_clk_sels)); + clk[IMX5_CLK_SSI3_ROOT_SEL] = imx_clk_mux("ssi3_root_sel", MXC_CCM_CSCMR1, 11, 1, ssi3_clk_sels, ARRAY_SIZE(ssi3_clk_sels)); + clk[IMX5_CLK_SSI_EXT1_SEL] = imx_clk_mux("ssi_ext1_sel", MXC_CCM_CSCMR1, 28, 2, ssi_clk_sels, ARRAY_SIZE(ssi_clk_sels)); + clk[IMX5_CLK_SSI_EXT2_SEL] = imx_clk_mux("ssi_ext2_sel", MXC_CCM_CSCMR1, 30, 2, ssi_clk_sels, ARRAY_SIZE(ssi_clk_sels)); + clk[IMX5_CLK_SSI_EXT1_COM_SEL] = imx_clk_mux("ssi_ext1_com_sel", MXC_CCM_CSCMR1, 0, 1, ssi_ext1_com_sels, ARRAY_SIZE(ssi_ext1_com_sels)); + clk[IMX5_CLK_SSI_EXT2_COM_SEL] = imx_clk_mux("ssi_ext2_com_sel", MXC_CCM_CSCMR1, 1, 1, ssi_ext2_com_sels, ARRAY_SIZE(ssi_ext2_com_sels)); + clk[IMX5_CLK_SSI1_ROOT_PRED] = imx_clk_divider("ssi1_root_pred", "ssi1_root_sel", MXC_CCM_CS1CDR, 6, 3); + clk[IMX5_CLK_SSI1_ROOT_PODF] = imx_clk_divider("ssi1_root_podf", "ssi1_root_pred", MXC_CCM_CS1CDR, 0, 6); + clk[IMX5_CLK_SSI2_ROOT_PRED] = imx_clk_divider("ssi2_root_pred", "ssi2_root_sel", MXC_CCM_CS2CDR, 6, 3); + clk[IMX5_CLK_SSI2_ROOT_PODF] = imx_clk_divider("ssi2_root_podf", "ssi2_root_pred", MXC_CCM_CS2CDR, 0, 6); + clk[IMX5_CLK_SSI_EXT1_PRED] = imx_clk_divider("ssi_ext1_pred", "ssi_ext1_sel", MXC_CCM_CS1CDR, 22, 3); + clk[IMX5_CLK_SSI_EXT1_PODF] = imx_clk_divider("ssi_ext1_podf", "ssi_ext1_pred", MXC_CCM_CS1CDR, 16, 6); + clk[IMX5_CLK_SSI_EXT2_PRED] = imx_clk_divider("ssi_ext2_pred", "ssi_ext2_sel", MXC_CCM_CS2CDR, 22, 3); + clk[IMX5_CLK_SSI_EXT2_PODF] = imx_clk_divider("ssi_ext2_podf", "ssi_ext2_pred", MXC_CCM_CS2CDR, 16, 6); + clk[IMX5_CLK_SSI1_ROOT_GATE] = imx_clk_gate2("ssi1_root_gate", "ssi1_root_podf", MXC_CCM_CCGR3, 18); + clk[IMX5_CLK_SSI2_ROOT_GATE] = imx_clk_gate2("ssi2_root_gate", "ssi2_root_podf", MXC_CCM_CCGR3, 22); + clk[IMX5_CLK_SSI3_ROOT_GATE] = imx_clk_gate2("ssi3_root_gate", "ssi3_root_sel", MXC_CCM_CCGR3, 26); + clk[IMX5_CLK_SSI_EXT1_GATE] = imx_clk_gate2("ssi_ext1_gate", "ssi_ext1_com_sel", MXC_CCM_CCGR3, 28); + clk[IMX5_CLK_SSI_EXT2_GATE] = imx_clk_gate2("ssi_ext2_gate", "ssi_ext2_com_sel", MXC_CCM_CCGR3, 30); + clk[IMX5_CLK_EPIT1_IPG_GATE] = imx_clk_gate2("epit1_ipg_gate", "ipg", MXC_CCM_CCGR2, 2); + clk[IMX5_CLK_EPIT1_HF_GATE] = imx_clk_gate2("epit1_hf_gate", "per_root", MXC_CCM_CCGR2, 4); + clk[IMX5_CLK_EPIT2_IPG_GATE] = imx_clk_gate2("epit2_ipg_gate", "ipg", MXC_CCM_CCGR2, 6); + clk[IMX5_CLK_EPIT2_HF_GATE] = imx_clk_gate2("epit2_hf_gate", "per_root", MXC_CCM_CCGR2, 8); + clk[IMX5_CLK_OWIRE_GATE] = imx_clk_gate2("owire_gate", "per_root", MXC_CCM_CCGR2, 22); + clk[IMX5_CLK_SRTC_GATE] = imx_clk_gate2("srtc_gate", "per_root", MXC_CCM_CCGR4, 28); + clk[IMX5_CLK_PATA_GATE] = imx_clk_gate2("pata_gate", "ipg", MXC_CCM_CCGR4, 0); + clk[IMX5_CLK_SPDIF0_SEL] = imx_clk_mux("spdif0_sel", MXC_CCM_CSCMR2, 0, 2, spdif_sel, ARRAY_SIZE(spdif_sel)); + clk[IMX5_CLK_SPDIF0_PRED] = imx_clk_divider("spdif0_pred", "spdif0_sel", MXC_CCM_CDCDR, 25, 3); + clk[IMX5_CLK_SPDIF0_PODF] = imx_clk_divider("spdif0_podf", "spdif0_pred", MXC_CCM_CDCDR, 19, 6); + clk[IMX5_CLK_SPDIF0_COM_SEL] = imx_clk_mux_flags("spdif0_com_sel", MXC_CCM_CSCMR2, 4, 1, + spdif0_com_sel, ARRAY_SIZE(spdif0_com_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_SPDIF0_GATE] = imx_clk_gate2("spdif0_gate", "spdif0_com_sel", MXC_CCM_CCGR5, 26); + clk[IMX5_CLK_SPDIF_IPG_GATE] = imx_clk_gate2("spdif_ipg_gate", "ipg", MXC_CCM_CCGR5, 30); + clk[IMX5_CLK_SAHARA_IPG_GATE] = imx_clk_gate2("sahara_ipg_gate", "ipg", MXC_CCM_CCGR4, 14); + clk[IMX5_CLK_SATA_REF] = imx_clk_fixed_factor("sata_ref", "usb_phy1_gate", 1, 1); + + clk_register_clkdev(clk[IMX5_CLK_CPU_PODF], NULL, "cpu0"); + clk_register_clkdev(clk[IMX5_CLK_GPC_DVFS], "gpc_dvfs", NULL); + + /* move usb phy clk to 24MHz */ + clk_set_parent(clk[IMX5_CLK_USB_PHY_SEL], clk[IMX5_CLK_OSC]); +} + +static void __init mx50_clocks_init(struct device_node *np) +{ + void __iomem *ccm_base; + void __iomem *pll_base; + unsigned long r; + + pll_base = ioremap(MX53_DPLL1_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL1_SW] = imx_clk_pllv2("pll1_sw", "osc", pll_base); + + pll_base = ioremap(MX53_DPLL2_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL2_SW] = imx_clk_pllv2("pll2_sw", "osc", pll_base); + + pll_base = ioremap(MX53_DPLL3_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL3_SW] = imx_clk_pllv2("pll3_sw", "osc", pll_base); + + ccm_base = of_iomap(np, 0); + WARN_ON(!ccm_base); + + mx5_clocks_common_init(ccm_base); + + /* + * This clock is called periph_clk in the i.MX50 Reference Manual, but + * it comes closest in scope to the main_bus_clk of i.MX51 and i.MX53 + */ + clk[IMX5_CLK_MAIN_BUS] = imx_clk_mux("main_bus", MXC_CCM_CBCDR, 25, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + + clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 10, 1, + lp_apm_sel, ARRAY_SIZE(lp_apm_sel)); + clk[IMX5_CLK_ESDHC_A_SEL] = imx_clk_mux("esdhc_a_sel", MXC_CCM_CSCMR1, 21, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_ESDHC_B_SEL] = imx_clk_mux("esdhc_b_sel", MXC_CCM_CSCMR1, 16, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_ESDHC_C_SEL] = imx_clk_mux("esdhc_c_sel", MXC_CCM_CSCMR1, 20, 1, esdhc_c_sel, ARRAY_SIZE(esdhc_c_sel)); + clk[IMX5_CLK_ESDHC_D_SEL] = imx_clk_mux("esdhc_d_sel", MXC_CCM_CSCMR1, 19, 1, esdhc_d_sel, ARRAY_SIZE(esdhc_d_sel)); + clk[IMX5_CLK_ESDHC1_PER_GATE] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2); + clk[IMX5_CLK_ESDHC2_PER_GATE] = imx_clk_gate2("esdhc2_per_gate", "esdhc_c_sel", MXC_CCM_CCGR3, 6); + clk[IMX5_CLK_ESDHC3_PER_GATE] = imx_clk_gate2("esdhc3_per_gate", "esdhc_b_podf", MXC_CCM_CCGR3, 10); + clk[IMX5_CLK_ESDHC4_PER_GATE] = imx_clk_gate2("esdhc4_per_gate", "esdhc_d_sel", MXC_CCM_CCGR3, 14); + clk[IMX5_CLK_USB_PHY1_GATE] = imx_clk_gate2("usb_phy1_gate", "usb_phy_sel", MXC_CCM_CCGR4, 10); + clk[IMX5_CLK_USB_PHY2_GATE] = imx_clk_gate2("usb_phy2_gate", "usb_phy_sel", MXC_CCM_CCGR4, 12); + clk[IMX5_CLK_I2C3_GATE] = imx_clk_gate2("i2c3_gate", "per_root", MXC_CCM_CCGR1, 22); + clk[IMX5_CLK_UART4_IPG_GATE] = imx_clk_gate2("uart4_ipg_gate", "ipg", MXC_CCM_CCGR7, 8); + clk[IMX5_CLK_UART4_PER_GATE] = imx_clk_gate2("uart4_per_gate", "uart_root", MXC_CCM_CCGR7, 10); + clk[IMX5_CLK_UART5_IPG_GATE] = imx_clk_gate2("uart5_ipg_gate", "ipg", MXC_CCM_CCGR7, 12); + clk[IMX5_CLK_UART5_PER_GATE] = imx_clk_gate2("uart5_per_gate", "uart_root", MXC_CCM_CCGR7, 14); + + clk[IMX5_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", MXC_CCM_CCOSR, 0, 4, + mx53_cko1_sel, ARRAY_SIZE(mx53_cko1_sel)); + clk[IMX5_CLK_CKO1_PODF] = imx_clk_divider("cko1_podf", "cko1_sel", MXC_CCM_CCOSR, 4, 3); + clk[IMX5_CLK_CKO1] = imx_clk_gate2("cko1", "cko1_podf", MXC_CCM_CCOSR, 7); + + clk[IMX5_CLK_CKO2_SEL] = imx_clk_mux("cko2_sel", MXC_CCM_CCOSR, 16, 5, + mx53_cko2_sel, ARRAY_SIZE(mx53_cko2_sel)); + clk[IMX5_CLK_CKO2_PODF] = imx_clk_divider("cko2_podf", "cko2_sel", MXC_CCM_CCOSR, 21, 3); + clk[IMX5_CLK_CKO2] = imx_clk_gate2("cko2", "cko2_podf", MXC_CCM_CCOSR, 24); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + /* Set SDHC parents to be PLL2 */ + clk_set_parent(clk[IMX5_CLK_ESDHC_A_SEL], clk[IMX5_CLK_PLL2_SW]); + clk_set_parent(clk[IMX5_CLK_ESDHC_B_SEL], clk[IMX5_CLK_PLL2_SW]); + + /* set SDHC root clock to 200MHZ*/ + clk_set_rate(clk[IMX5_CLK_ESDHC_A_PODF], 200000000); + clk_set_rate(clk[IMX5_CLK_ESDHC_B_PODF], 200000000); + + clk_prepare_enable(clk[IMX5_CLK_IIM_GATE]); + imx_print_silicon_rev("i.MX50", IMX_CHIP_REVISION_1_1); + clk_disable_unprepare(clk[IMX5_CLK_IIM_GATE]); + + r = clk_round_rate(clk[IMX5_CLK_USBOH3_PER_GATE], 54000000); + clk_set_rate(clk[IMX5_CLK_USBOH3_PER_GATE], r); + + imx_register_uart_clocks(5); +} +CLK_OF_DECLARE(imx50_ccm, "fsl,imx50-ccm", mx50_clocks_init); + +static void __init mx51_clocks_init(struct device_node *np) +{ + void __iomem *ccm_base; + void __iomem *pll_base; + u32 val; + + pll_base = ioremap(MX51_DPLL1_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL1_SW] = imx_clk_pllv2("pll1_sw", "osc", pll_base); + + pll_base = ioremap(MX51_DPLL2_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL2_SW] = imx_clk_pllv2("pll2_sw", "osc", pll_base); + + pll_base = ioremap(MX51_DPLL3_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL3_SW] = imx_clk_pllv2("pll3_sw", "osc", pll_base); + + ccm_base = of_iomap(np, 0); + WARN_ON(!ccm_base); + + mx5_clocks_common_init(ccm_base); + + clk[IMX5_CLK_PERIPH_APM] = imx_clk_mux("periph_apm", MXC_CCM_CBCMR, 12, 2, + periph_apm_sel, ARRAY_SIZE(periph_apm_sel)); + clk[IMX5_CLK_MAIN_BUS] = imx_clk_mux("main_bus", MXC_CCM_CBCDR, 25, 1, + main_bus_sel, ARRAY_SIZE(main_bus_sel)); + clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 9, 1, + lp_apm_sel, ARRAY_SIZE(lp_apm_sel)); + clk[IMX5_CLK_IPU_DI0_SEL] = imx_clk_mux_flags("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3, + mx51_ipu_di0_sel, ARRAY_SIZE(mx51_ipu_di0_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_IPU_DI1_SEL] = imx_clk_mux_flags("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3, + mx51_ipu_di1_sel, ARRAY_SIZE(mx51_ipu_di1_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_TVE_EXT_SEL] = imx_clk_mux_flags("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1, + mx51_tve_ext_sel, ARRAY_SIZE(mx51_tve_ext_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_TVE_SEL] = imx_clk_mux("tve_sel", MXC_CCM_CSCMR1, 7, 1, + mx51_tve_sel, ARRAY_SIZE(mx51_tve_sel)); + clk[IMX5_CLK_TVE_GATE] = imx_clk_gate2("tve_gate", "tve_sel", MXC_CCM_CCGR2, 30); + clk[IMX5_CLK_TVE_PRED] = imx_clk_divider("tve_pred", "pll3_sw", MXC_CCM_CDCDR, 28, 3); + clk[IMX5_CLK_ESDHC_A_SEL] = imx_clk_mux("esdhc_a_sel", MXC_CCM_CSCMR1, 20, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_ESDHC_B_SEL] = imx_clk_mux("esdhc_b_sel", MXC_CCM_CSCMR1, 16, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_ESDHC_C_SEL] = imx_clk_mux("esdhc_c_sel", MXC_CCM_CSCMR1, 19, 1, esdhc_c_sel, ARRAY_SIZE(esdhc_c_sel)); + clk[IMX5_CLK_ESDHC_D_SEL] = imx_clk_mux("esdhc_d_sel", MXC_CCM_CSCMR1, 18, 1, esdhc_d_sel, ARRAY_SIZE(esdhc_d_sel)); + clk[IMX5_CLK_ESDHC1_PER_GATE] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2); + clk[IMX5_CLK_ESDHC2_PER_GATE] = imx_clk_gate2("esdhc2_per_gate", "esdhc_b_podf", MXC_CCM_CCGR3, 6); + clk[IMX5_CLK_ESDHC3_PER_GATE] = imx_clk_gate2("esdhc3_per_gate", "esdhc_c_sel", MXC_CCM_CCGR3, 10); + clk[IMX5_CLK_ESDHC4_PER_GATE] = imx_clk_gate2("esdhc4_per_gate", "esdhc_d_sel", MXC_CCM_CCGR3, 14); + clk[IMX5_CLK_USB_PHY_GATE] = imx_clk_gate2("usb_phy_gate", "usb_phy_sel", MXC_CCM_CCGR2, 0); + clk[IMX5_CLK_HSI2C_GATE] = imx_clk_gate2("hsi2c_gate", "ipg", MXC_CCM_CCGR1, 22); + clk[IMX5_CLK_SCC2_IPG_GATE] = imx_clk_gate2("scc2_gate", "ipg", MXC_CCM_CCGR1, 30); + clk[IMX5_CLK_MIPI_HSC1_GATE] = imx_clk_gate2_flags("mipi_hsc1_gate", "ipg", MXC_CCM_CCGR4, 6, CLK_IS_CRITICAL); + clk[IMX5_CLK_MIPI_HSC2_GATE] = imx_clk_gate2_flags("mipi_hsc2_gate", "ipg", MXC_CCM_CCGR4, 8, CLK_IS_CRITICAL); + clk[IMX5_CLK_MIPI_ESC_GATE] = imx_clk_gate2_flags("mipi_esc_gate", "ipg", MXC_CCM_CCGR4, 10, CLK_IS_CRITICAL); + clk[IMX5_CLK_MIPI_HSP_GATE] = imx_clk_gate2_flags("mipi_hsp_gate", "ipg", MXC_CCM_CCGR4, 12, CLK_IS_CRITICAL); + clk[IMX5_CLK_SPDIF_XTAL_SEL] = imx_clk_mux("spdif_xtal_sel", MXC_CCM_CSCMR1, 2, 2, + mx51_spdif_xtal_sel, ARRAY_SIZE(mx51_spdif_xtal_sel)); + clk[IMX5_CLK_SPDIF1_SEL] = imx_clk_mux("spdif1_sel", MXC_CCM_CSCMR2, 2, 2, + spdif_sel, ARRAY_SIZE(spdif_sel)); + clk[IMX5_CLK_SPDIF1_PRED] = imx_clk_divider("spdif1_pred", "spdif1_sel", MXC_CCM_CDCDR, 16, 3); + clk[IMX5_CLK_SPDIF1_PODF] = imx_clk_divider("spdif1_podf", "spdif1_pred", MXC_CCM_CDCDR, 9, 6); + clk[IMX5_CLK_SPDIF1_COM_SEL] = imx_clk_mux("spdif1_com_sel", MXC_CCM_CSCMR2, 5, 1, + mx51_spdif1_com_sel, ARRAY_SIZE(mx51_spdif1_com_sel)); + clk[IMX5_CLK_SPDIF1_GATE] = imx_clk_gate2("spdif1_gate", "spdif1_com_sel", MXC_CCM_CCGR5, 28); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + /* set the usboh3 parent to pll2_sw */ + clk_set_parent(clk[IMX5_CLK_USBOH3_SEL], clk[IMX5_CLK_PLL2_SW]); + + /* Set SDHC parents to be PLL2 */ + clk_set_parent(clk[IMX5_CLK_ESDHC_A_SEL], clk[IMX5_CLK_PLL2_SW]); + clk_set_parent(clk[IMX5_CLK_ESDHC_B_SEL], clk[IMX5_CLK_PLL2_SW]); + + /* set SDHC root clock to 166.25MHZ*/ + clk_set_rate(clk[IMX5_CLK_ESDHC_A_PODF], 166250000); + clk_set_rate(clk[IMX5_CLK_ESDHC_B_PODF], 166250000); + + clk_prepare_enable(clk[IMX5_CLK_IIM_GATE]); + imx_print_silicon_rev("i.MX51", mx51_revision()); + clk_disable_unprepare(clk[IMX5_CLK_IIM_GATE]); + + /* + * Reference Manual says: Functionality of CCDR[18] and CLPCR[23] is no + * longer supported. Set to one for better power saving. + * + * The effect of not setting these bits is that MIPI clocks can't be + * enabled without the IPU clock being enabled aswell. + */ + val = readl(MXC_CCM_CCDR); + val |= 1 << 18; + writel(val, MXC_CCM_CCDR); + + val = readl(MXC_CCM_CLPCR); + val |= 1 << 23; + writel(val, MXC_CCM_CLPCR); + + imx_register_uart_clocks(3); +} +CLK_OF_DECLARE(imx51_ccm, "fsl,imx51-ccm", mx51_clocks_init); + +static void __init mx53_clocks_init(struct device_node *np) +{ + void __iomem *ccm_base; + void __iomem *pll_base; + unsigned long r; + + pll_base = ioremap(MX53_DPLL1_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL1_SW] = imx_clk_pllv2("pll1_sw", "osc", pll_base); + + pll_base = ioremap(MX53_DPLL2_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL2_SW] = imx_clk_pllv2("pll2_sw", "osc", pll_base); + + pll_base = ioremap(MX53_DPLL3_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL3_SW] = imx_clk_pllv2("pll3_sw", "osc", pll_base); + + pll_base = ioremap(MX53_DPLL4_BASE, SZ_16K); + WARN_ON(!pll_base); + clk[IMX5_CLK_PLL4_SW] = imx_clk_pllv2("pll4_sw", "osc", pll_base); + + ccm_base = of_iomap(np, 0); + WARN_ON(!ccm_base); + + mx5_clocks_common_init(ccm_base); + + clk[IMX5_CLK_PERIPH_APM] = imx_clk_mux("periph_apm", MXC_CCM_CBCMR, 12, 2, + periph_apm_sel, ARRAY_SIZE(periph_apm_sel)); + clk[IMX5_CLK_MAIN_BUS] = imx_clk_mux("main_bus", MXC_CCM_CBCDR, 25, 1, + main_bus_sel, ARRAY_SIZE(main_bus_sel)); + clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 10, 1, + lp_apm_sel, ARRAY_SIZE(lp_apm_sel)); + clk[IMX5_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); + clk[IMX5_CLK_LDB_DI1_DIV] = imx_clk_divider_flags("ldb_di1_div", "ldb_di1_div_3_5", MXC_CCM_CSCMR2, 11, 1, 0); + clk[IMX5_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", MXC_CCM_CSCMR2, 9, 1, + mx53_ldb_di1_sel, ARRAY_SIZE(mx53_ldb_di1_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_DI_PLL4_PODF] = imx_clk_divider("di_pll4_podf", "pll4_sw", MXC_CCM_CDCDR, 16, 3); + clk[IMX5_CLK_LDB_DI0_DIV_3_5] = imx_clk_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); + clk[IMX5_CLK_LDB_DI0_DIV] = imx_clk_divider_flags("ldb_di0_div", "ldb_di0_div_3_5", MXC_CCM_CSCMR2, 10, 1, 0); + clk[IMX5_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", MXC_CCM_CSCMR2, 8, 1, + mx53_ldb_di0_sel, ARRAY_SIZE(mx53_ldb_di0_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_LDB_DI0_GATE] = imx_clk_gate2("ldb_di0_gate", "ldb_di0_div", MXC_CCM_CCGR6, 28); + clk[IMX5_CLK_LDB_DI1_GATE] = imx_clk_gate2("ldb_di1_gate", "ldb_di1_div", MXC_CCM_CCGR6, 30); + clk[IMX5_CLK_IPU_DI0_SEL] = imx_clk_mux_flags("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3, + mx53_ipu_di0_sel, ARRAY_SIZE(mx53_ipu_di0_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_IPU_DI1_SEL] = imx_clk_mux_flags("ipu_di1_sel", MXC_CCM_CSCMR2, 29, 3, + mx53_ipu_di1_sel, ARRAY_SIZE(mx53_ipu_di1_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_TVE_EXT_SEL] = imx_clk_mux_flags("tve_ext_sel", MXC_CCM_CSCMR1, 6, 1, + mx53_tve_ext_sel, ARRAY_SIZE(mx53_tve_ext_sel), CLK_SET_RATE_PARENT); + clk[IMX5_CLK_TVE_GATE] = imx_clk_gate2("tve_gate", "tve_pred", MXC_CCM_CCGR2, 30); + clk[IMX5_CLK_TVE_PRED] = imx_clk_divider("tve_pred", "tve_ext_sel", MXC_CCM_CDCDR, 28, 3); + clk[IMX5_CLK_ESDHC_A_SEL] = imx_clk_mux("esdhc_a_sel", MXC_CCM_CSCMR1, 20, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_ESDHC_B_SEL] = imx_clk_mux("esdhc_b_sel", MXC_CCM_CSCMR1, 16, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_ESDHC_C_SEL] = imx_clk_mux("esdhc_c_sel", MXC_CCM_CSCMR1, 19, 1, esdhc_c_sel, ARRAY_SIZE(esdhc_c_sel)); + clk[IMX5_CLK_ESDHC_D_SEL] = imx_clk_mux("esdhc_d_sel", MXC_CCM_CSCMR1, 18, 1, esdhc_d_sel, ARRAY_SIZE(esdhc_d_sel)); + clk[IMX5_CLK_ESDHC1_PER_GATE] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2); + clk[IMX5_CLK_ESDHC2_PER_GATE] = imx_clk_gate2("esdhc2_per_gate", "esdhc_c_sel", MXC_CCM_CCGR3, 6); + clk[IMX5_CLK_ESDHC3_PER_GATE] = imx_clk_gate2("esdhc3_per_gate", "esdhc_b_podf", MXC_CCM_CCGR3, 10); + clk[IMX5_CLK_ESDHC4_PER_GATE] = imx_clk_gate2("esdhc4_per_gate", "esdhc_d_sel", MXC_CCM_CCGR3, 14); + clk[IMX5_CLK_USB_PHY1_GATE] = imx_clk_gate2("usb_phy1_gate", "usb_phy_sel", MXC_CCM_CCGR4, 10); + clk[IMX5_CLK_USB_PHY2_GATE] = imx_clk_gate2("usb_phy2_gate", "usb_phy_sel", MXC_CCM_CCGR4, 12); + clk[IMX5_CLK_CAN_SEL] = imx_clk_mux("can_sel", MXC_CCM_CSCMR2, 6, 2, + mx53_can_sel, ARRAY_SIZE(mx53_can_sel)); + clk[IMX5_CLK_CAN1_SERIAL_GATE] = imx_clk_gate2("can1_serial_gate", "can_sel", MXC_CCM_CCGR6, 22); + clk[IMX5_CLK_CAN1_IPG_GATE] = imx_clk_gate2("can1_ipg_gate", "ipg", MXC_CCM_CCGR6, 20); + clk[IMX5_CLK_OCRAM] = imx_clk_gate2("ocram", "ahb", MXC_CCM_CCGR6, 2); + clk[IMX5_CLK_CAN2_SERIAL_GATE] = imx_clk_gate2("can2_serial_gate", "can_sel", MXC_CCM_CCGR4, 8); + clk[IMX5_CLK_CAN2_IPG_GATE] = imx_clk_gate2("can2_ipg_gate", "ipg", MXC_CCM_CCGR4, 6); + clk[IMX5_CLK_I2C3_GATE] = imx_clk_gate2("i2c3_gate", "per_root", MXC_CCM_CCGR1, 22); + clk[IMX5_CLK_SATA_GATE] = imx_clk_gate2("sata_gate", "ipg", MXC_CCM_CCGR4, 2); + + clk[IMX5_CLK_FIRI_SEL] = imx_clk_mux("firi_sel", MXC_CCM_CSCMR2, 12, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_FIRI_PRED] = imx_clk_divider("firi_pred", "firi_sel", MXC_CCM_CSCDR3, 6, 3); + clk[IMX5_CLK_FIRI_PODF] = imx_clk_divider("firi_podf", "firi_pred", MXC_CCM_CSCDR3, 0, 6); + clk[IMX5_CLK_FIRI_SERIAL_GATE] = imx_clk_gate2("firi_serial_gate", "firi_podf", MXC_CCM_CCGR1, 28); + clk[IMX5_CLK_FIRI_IPG_GATE] = imx_clk_gate2("firi_ipg_gate", "ipg", MXC_CCM_CCGR1, 26); + + clk[IMX5_CLK_CSI0_MCLK1_SEL] = imx_clk_mux("csi0_mclk1_sel", MXC_CCM_CSCMR2, 22, 2, + standard_pll_sel, ARRAY_SIZE(standard_pll_sel)); + clk[IMX5_CLK_CSI0_MCLK1_PRED] = imx_clk_divider("csi0_mclk1_pred", "csi0_mclk1_sel", MXC_CCM_CSCDR4, 6, 3); + clk[IMX5_CLK_CSI0_MCLK1_PODF] = imx_clk_divider("csi0_mclk1_podf", "csi0_mclk1_pred", MXC_CCM_CSCDR4, 0, 6); + clk[IMX5_CLK_CSI0_MCLK1_GATE] = imx_clk_gate2("csi0_mclk1_serial_gate", "csi0_mclk1_podf", MXC_CCM_CCGR6, 4); + + clk[IMX5_CLK_IEEE1588_SEL] = imx_clk_mux("ieee1588_sel", MXC_CCM_CSCMR2, 14, 2, + ieee1588_sels, ARRAY_SIZE(ieee1588_sels)); + clk[IMX5_CLK_IEEE1588_PRED] = imx_clk_divider("ieee1588_pred", "ieee1588_sel", MXC_CCM_CSCDR2, 6, 3); + clk[IMX5_CLK_IEEE1588_PODF] = imx_clk_divider("ieee1588_podf", "ieee1588_pred", MXC_CCM_CSCDR2, 0, 6); + clk[IMX5_CLK_IEEE1588_GATE] = imx_clk_gate2("ieee1588_serial_gate", "ieee1588_podf", MXC_CCM_CCGR7, 6); + clk[IMX5_CLK_UART4_IPG_GATE] = imx_clk_gate2("uart4_ipg_gate", "ipg", MXC_CCM_CCGR7, 8); + clk[IMX5_CLK_UART4_PER_GATE] = imx_clk_gate2("uart4_per_gate", "uart_root", MXC_CCM_CCGR7, 10); + clk[IMX5_CLK_UART5_IPG_GATE] = imx_clk_gate2("uart5_ipg_gate", "ipg", MXC_CCM_CCGR7, 12); + clk[IMX5_CLK_UART5_PER_GATE] = imx_clk_gate2("uart5_per_gate", "uart_root", MXC_CCM_CCGR7, 14); + + clk[IMX5_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", MXC_CCM_CCOSR, 0, 4, + mx53_cko1_sel, ARRAY_SIZE(mx53_cko1_sel)); + clk[IMX5_CLK_CKO1_PODF] = imx_clk_divider("cko1_podf", "cko1_sel", MXC_CCM_CCOSR, 4, 3); + clk[IMX5_CLK_CKO1] = imx_clk_gate2("cko1", "cko1_podf", MXC_CCM_CCOSR, 7); + + clk[IMX5_CLK_CKO2_SEL] = imx_clk_mux("cko2_sel", MXC_CCM_CCOSR, 16, 5, + mx53_cko2_sel, ARRAY_SIZE(mx53_cko2_sel)); + clk[IMX5_CLK_CKO2_PODF] = imx_clk_divider("cko2_podf", "cko2_sel", MXC_CCM_CCOSR, 21, 3); + clk[IMX5_CLK_CKO2] = imx_clk_gate2("cko2", "cko2_podf", MXC_CCM_CCOSR, 24); + clk[IMX5_CLK_SPDIF_XTAL_SEL] = imx_clk_mux("spdif_xtal_sel", MXC_CCM_CSCMR1, 2, 2, + mx53_spdif_xtal_sel, ARRAY_SIZE(mx53_spdif_xtal_sel)); + clk[IMX5_CLK_ARM] = imx_clk_cpu("arm", "cpu_podf", + clk[IMX5_CLK_CPU_PODF], + clk[IMX5_CLK_CPU_PODF_SEL], + clk[IMX5_CLK_PLL1_SW], + clk[IMX5_CLK_STEP_SEL]); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + /* Set SDHC parents to be PLL2 */ + clk_set_parent(clk[IMX5_CLK_ESDHC_A_SEL], clk[IMX5_CLK_PLL2_SW]); + clk_set_parent(clk[IMX5_CLK_ESDHC_B_SEL], clk[IMX5_CLK_PLL2_SW]); + + /* set SDHC root clock to 200MHZ*/ + clk_set_rate(clk[IMX5_CLK_ESDHC_A_PODF], 200000000); + clk_set_rate(clk[IMX5_CLK_ESDHC_B_PODF], 200000000); + + /* move can bus clk to 24MHz */ + clk_set_parent(clk[IMX5_CLK_CAN_SEL], clk[IMX5_CLK_LP_APM]); + + /* make sure step clock is running from 24MHz */ + clk_set_parent(clk[IMX5_CLK_STEP_SEL], clk[IMX5_CLK_LP_APM]); + + clk_prepare_enable(clk[IMX5_CLK_IIM_GATE]); + imx_print_silicon_rev("i.MX53", mx53_revision()); + clk_disable_unprepare(clk[IMX5_CLK_IIM_GATE]); + + r = clk_round_rate(clk[IMX5_CLK_USBOH3_PER_GATE], 54000000); + clk_set_rate(clk[IMX5_CLK_USBOH3_PER_GATE], r); + + imx_register_uart_clocks(5); +} +CLK_OF_DECLARE(imx53_ccm, "fsl,imx53-ccm", mx53_clocks_init); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c new file mode 100644 index 000000000..de36f58d5 --- /dev/null +++ b/drivers/clk/imx/clk-imx6q.c @@ -0,0 +1,979 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2011-2013 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + */ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <soc/imx/revision.h> +#include <dt-bindings/clock/imx6qdl-clock.h> + +#include "clk.h" + +static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; +static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; +static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; +static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; +static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; +static const char *axi_sels[] = { "periph", "pll2_pfd2_396m", "periph", "pll3_pfd1_540m", }; +static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll3_pfd3_454m", "pll3_usb_otg", }; +static const char *gpu_axi_sels[] = { "axi", "ahb", }; +static const char *pre_axi_sels[] = { "axi", "ahb", }; +static const char *gpu2d_core_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", }; +static const char *gpu2d_core_sels_2[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll3_pfd0_720m",}; +static const char *gpu3d_core_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll2_pfd2_396m", }; +static const char *gpu3d_shader_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll2_pfd1_594m", "pll3_pfd0_720m", }; +static const char *ipu_sels[] = { "mmdc_ch0_axi", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; +static const char *ldb_di_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "mmdc_ch1_axi", "pll3_usb_otg", }; +static const char *ipu_di_pre_sels[] = { "mmdc_ch0_axi", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd1_540m", }; +static const char *ipu1_di0_sels[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; +static const char *ipu1_di1_sels[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; +static const char *ipu2_di0_sels[] = { "ipu2_di0_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; +static const char *ipu2_di1_sels[] = { "ipu2_di1_pre", "dummy", "dummy", "ldb_di0", "ldb_di1", }; +static const char *ipu1_di0_sels_2[] = { "ipu1_di0_pre", "dummy", "dummy", "ldb_di0_podf", "ldb_di1_podf", }; +static const char *ipu1_di1_sels_2[] = { "ipu1_di1_pre", "dummy", "dummy", "ldb_di0_podf", "ldb_di1_podf", }; +static const char *ipu2_di0_sels_2[] = { "ipu2_di0_pre", "dummy", "dummy", "ldb_di0_podf", "ldb_di1_podf", }; +static const char *ipu2_di1_sels_2[] = { "ipu2_di1_pre", "dummy", "dummy", "ldb_di0_podf", "ldb_di1_podf", }; +static const char *hsi_tx_sels[] = { "pll3_120m", "pll2_pfd2_396m", }; +static const char *pcie_axi_sels[] = { "axi", "ahb", }; +static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio_div", }; +static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *enfc_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", }; +static const char *enfc_sels_2[] = {"pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", "pll3_pfd3_454m", "dummy", }; +static const char *eim_sels[] = { "pll2_pfd2_396m", "pll3_usb_otg", "axi", "pll2_pfd0_352m", }; +static const char *eim_slow_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *vdo_axi_sels[] = { "axi", "ahb", }; +static const char *vpu_axi_sels[] = { "axi", "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *uart_sels[] = { "pll3_80m", "osc", }; +static const char *ipg_per_sels[] = { "ipg", "osc", }; +static const char *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char *can_sels[] = { "pll3_60m", "osc", "pll3_80m", }; +static const char *cko1_sels[] = { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div", + "video_27m", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0", + "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio_div", }; +static const char *cko2_sels[] = { + "mmdc_ch0_axi", "mmdc_ch1_axi", "usdhc4", "usdhc1", + "gpu2d_axi", "dummy", "ecspi_root", "gpu3d_axi", + "usdhc3", "dummy", "arm", "ipu1", + "ipu2", "vdo_axi", "osc", "gpu2d_core", + "gpu3d_core", "usdhc2", "ssi1", "ssi2", + "ssi3", "gpu3d_shader", "vpu_axi", "can_root", + "ldb_di0", "ldb_di1", "esai_extal", "eim_slow", + "uart_serial", "spdif", "asrc", "hsi_tx", +}; +static const char *cko_sels[] = { "cko1", "cko2", }; +static const char *lvds_sels[] = { + "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", + "pll4_audio", "pll5_video", "pll8_mlb", "enet_ref", + "pcie_ref_125m", "sata_ref_100m", "usbphy1", "usbphy2", + "dummy", "dummy", "dummy", "dummy", "osc", +}; +static const char *pll_bypass_src_sels[] = { "osc", "lvds1_in", "lvds2_in", "dummy", }; +static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", }; +static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", }; +static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", }; +static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", }; +static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; +static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; +static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; + +static struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, + { /* sentinel */ } +}; + +static struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { /* sentinel */ } +}; + +static struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { /* sentinel */ } +}; + +static unsigned int share_count_esai; +static unsigned int share_count_asrc; +static unsigned int share_count_ssi1; +static unsigned int share_count_ssi2; +static unsigned int share_count_ssi3; +static unsigned int share_count_mipi_core_cfg; +static unsigned int share_count_spdif; +static unsigned int share_count_prg0; +static unsigned int share_count_prg1; + +static inline int clk_on_imx6q(void) +{ + return of_machine_is_compatible("fsl,imx6q"); +} + +static inline int clk_on_imx6qp(void) +{ + return of_machine_is_compatible("fsl,imx6qp"); +} + +static inline int clk_on_imx6dl(void) +{ + return of_machine_is_compatible("fsl,imx6dl"); +} + +static int ldb_di_sel_by_clock_id(int clock_id) +{ + switch (clock_id) { + case IMX6QDL_CLK_PLL5_VIDEO_DIV: + if (clk_on_imx6q() && + imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) + return -ENOENT; + return 0; + case IMX6QDL_CLK_PLL2_PFD0_352M: + return 1; + case IMX6QDL_CLK_PLL2_PFD2_396M: + return 2; + case IMX6QDL_CLK_MMDC_CH1_AXI: + return 3; + case IMX6QDL_CLK_PLL3_USB_OTG: + return 4; + default: + return -ENOENT; + } +} + +static void of_assigned_ldb_sels(struct device_node *node, + unsigned int *ldb_di0_sel, + unsigned int *ldb_di1_sel) +{ + struct of_phandle_args clkspec; + int index, rc, num_parents; + int parent, child, sel; + + num_parents = of_count_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells"); + for (index = 0; index < num_parents; index++) { + rc = of_parse_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return; + } + if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { + pr_err("ccm: parent clock %d not in ccm\n", index); + return; + } + parent = clkspec.args[0]; + + rc = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) + return; + if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) { + pr_err("ccm: child clock %d not in ccm\n", index); + return; + } + child = clkspec.args[0]; + + if (child != IMX6QDL_CLK_LDB_DI0_SEL && + child != IMX6QDL_CLK_LDB_DI1_SEL) + continue; + + sel = ldb_di_sel_by_clock_id(parent); + if (sel < 0) { + pr_err("ccm: invalid ldb_di%d parent clock: %d\n", + child == IMX6QDL_CLK_LDB_DI1_SEL, parent); + continue; + } + + if (child == IMX6QDL_CLK_LDB_DI0_SEL) + *ldb_di0_sel = sel; + if (child == IMX6QDL_CLK_LDB_DI1_SEL) + *ldb_di1_sel = sel; + } +} + +static bool pll6_bypassed(struct device_node *node) +{ + int index, ret, num_clocks; + struct of_phandle_args clkspec; + + num_clocks = of_count_phandle_with_args(node, "assigned-clocks", + "#clock-cells"); + if (num_clocks < 0) + return false; + + for (index = 0; index < num_clocks; index++) { + ret = of_parse_phandle_with_args(node, "assigned-clocks", + "#clock-cells", index, + &clkspec); + if (ret < 0) + return false; + + if (clkspec.np == node && + clkspec.args[0] == IMX6QDL_PLL6_BYPASS) + break; + } + + /* PLL6 bypass is not part of the assigned clock list */ + if (index == num_clocks) + return false; + + ret = of_parse_phandle_with_args(node, "assigned-clock-parents", + "#clock-cells", index, &clkspec); + + if (clkspec.args[0] != IMX6QDL_CLK_PLL6) + return true; + + return false; +} + +#define CCM_CCSR 0x0c +#define CCM_CS2CDR 0x2c + +#define CCSR_PLL3_SW_CLK_SEL BIT(0) + +#define CS2CDR_LDB_DI0_CLK_SEL_SHIFT 9 +#define CS2CDR_LDB_DI1_CLK_SEL_SHIFT 12 + +/* + * The only way to disable the MMDC_CH1 clock is to move it to pll3_sw_clk + * via periph2_clk2_sel and then to disable pll3_sw_clk by selecting the + * bypass clock source, since there is no CG bit for mmdc_ch1. + */ +static void mmdc_ch1_disable(void __iomem *ccm_base) +{ + unsigned int reg; + + clk_set_parent(hws[IMX6QDL_CLK_PERIPH2_CLK2_SEL]->clk, + hws[IMX6QDL_CLK_PLL3_USB_OTG]->clk); + + /* Disable pll3_sw_clk by selecting the bypass clock source */ + reg = readl_relaxed(ccm_base + CCM_CCSR); + reg |= CCSR_PLL3_SW_CLK_SEL; + writel_relaxed(reg, ccm_base + CCM_CCSR); +} + +static void mmdc_ch1_reenable(void __iomem *ccm_base) +{ + unsigned int reg; + + /* Enable pll3_sw_clk by disabling the bypass */ + reg = readl_relaxed(ccm_base + CCM_CCSR); + reg &= ~CCSR_PLL3_SW_CLK_SEL; + writel_relaxed(reg, ccm_base + CCM_CCSR); +} + +/* + * We have to follow a strict procedure when changing the LDB clock source, + * otherwise we risk introducing a glitch that can lock up the LDB divider. + * Things to keep in mind: + * + * 1. The current and new parent clock inputs to the mux must be disabled. + * 2. The default clock input for ldb_di0/1_clk_sel is mmdc_ch1_axi, which + * has no CG bit. + * 3. pll2_pfd2_396m can not be gated if it is used as memory clock. + * 4. In the RTL implementation of the LDB_DI_CLK_SEL muxes the top four + * options are in one mux and the PLL3 option along with three unused + * inputs is in a second mux. There is a third mux with two inputs used + * to decide between the first and second 4-port mux: + * + * pll5_video_div 0 --|\ + * pll2_pfd0_352m 1 --| |_ + * pll2_pfd2_396m 2 --| | `-|\ + * mmdc_ch1_axi 3 --|/ | | + * | |-- + * pll3_usb_otg 4 --|\ | | + * 5 --| |_,-|/ + * 6 --| | + * 7 --|/ + * + * The ldb_di0/1_clk_sel[1:0] bits control both 4-port muxes at the same time. + * The ldb_di0/1_clk_sel[2] bit controls the 2-port mux. The code below + * switches the parent to the bottom mux first and then manipulates the top + * mux to ensure that no glitch will enter the divider. + */ +static void init_ldb_clks(struct device_node *np, void __iomem *ccm_base) +{ + unsigned int reg; + unsigned int sel[2][4]; + int i; + + reg = readl_relaxed(ccm_base + CCM_CS2CDR); + sel[0][0] = (reg >> CS2CDR_LDB_DI0_CLK_SEL_SHIFT) & 7; + sel[1][0] = (reg >> CS2CDR_LDB_DI1_CLK_SEL_SHIFT) & 7; + + sel[0][3] = sel[0][2] = sel[0][1] = sel[0][0]; + sel[1][3] = sel[1][2] = sel[1][1] = sel[1][0]; + + of_assigned_ldb_sels(np, &sel[0][3], &sel[1][3]); + + for (i = 0; i < 2; i++) { + /* Print a notice if a glitch might have been introduced already */ + if (sel[i][0] != 3) { + pr_notice("ccm: possible glitch: ldb_di%d_sel already changed from reset value: %d\n", + i, sel[i][0]); + } + + if (sel[i][0] == sel[i][3]) + continue; + + /* Only switch to or from pll2_pfd2_396m if it is disabled */ + if ((sel[i][0] == 2 || sel[i][3] == 2) && + (clk_get_parent(hws[IMX6QDL_CLK_PERIPH_PRE]->clk) == + hws[IMX6QDL_CLK_PLL2_PFD2_396M]->clk)) { + pr_err("ccm: ldb_di%d_sel: couldn't disable pll2_pfd2_396m\n", + i); + sel[i][3] = sel[i][2] = sel[i][1] = sel[i][0]; + continue; + } + + /* First switch to the bottom mux */ + sel[i][1] = sel[i][0] | 4; + + /* Then configure the top mux before switching back to it */ + sel[i][2] = sel[i][3] | 4; + + pr_debug("ccm: switching ldb_di%d_sel: %d->%d->%d->%d\n", i, + sel[i][0], sel[i][1], sel[i][2], sel[i][3]); + } + + if (sel[0][0] == sel[0][3] && sel[1][0] == sel[1][3]) + return; + + mmdc_ch1_disable(ccm_base); + + for (i = 1; i < 4; i++) { + reg = readl_relaxed(ccm_base + CCM_CS2CDR); + reg &= ~((7 << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) | + (7 << CS2CDR_LDB_DI1_CLK_SEL_SHIFT)); + reg |= ((sel[0][i] << CS2CDR_LDB_DI0_CLK_SEL_SHIFT) | + (sel[1][i] << CS2CDR_LDB_DI1_CLK_SEL_SHIFT)); + writel_relaxed(reg, ccm_base + CCM_CS2CDR); + } + + mmdc_ch1_reenable(ccm_base); +} + +#define CCM_ANALOG_PLL_VIDEO 0xa0 +#define CCM_ANALOG_PFD_480 0xf0 +#define CCM_ANALOG_PFD_528 0x100 + +#define PLL_ENABLE BIT(13) + +#define PFD0_CLKGATE BIT(7) +#define PFD1_CLKGATE BIT(15) +#define PFD2_CLKGATE BIT(23) +#define PFD3_CLKGATE BIT(31) + +static void disable_anatop_clocks(void __iomem *anatop_base) +{ + unsigned int reg; + + /* Make sure PLL2 PFDs 0-2 are gated */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_528); + /* Cannot gate PFD2 if pll2_pfd2_396m is the parent of MMDC clock */ + if (clk_get_parent(hws[IMX6QDL_CLK_PERIPH_PRE]->clk) == + hws[IMX6QDL_CLK_PLL2_PFD2_396M]->clk) + reg |= PFD0_CLKGATE | PFD1_CLKGATE; + else + reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_528); + + /* Make sure PLL3 PFDs 0-3 are gated */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PFD_480); + reg |= PFD0_CLKGATE | PFD1_CLKGATE | PFD2_CLKGATE | PFD3_CLKGATE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PFD_480); + + /* Make sure PLL5 is disabled */ + reg = readl_relaxed(anatop_base + CCM_ANALOG_PLL_VIDEO); + reg &= ~PLL_ENABLE; + writel_relaxed(reg, anatop_base + CCM_ANALOG_PLL_VIDEO); +} + +static struct clk_hw * __init imx6q_obtain_fixed_clk_hw(struct device_node *np, + const char *name, + unsigned long rate) +{ + struct clk *clk = of_clk_get_by_name(np, name); + struct clk_hw *hw; + + if (IS_ERR(clk)) + hw = imx_obtain_fixed_clock_hw(name, rate); + else + hw = __clk_get_hw(clk); + + return hw; +} + +static void __init imx6q_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *anatop_base, *base; + int ret; + + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + IMX6QDL_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return; + + clk_hw_data->num = IMX6QDL_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX6QDL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + hws[IMX6QDL_CLK_CKIL] = imx6q_obtain_fixed_clk_hw(ccm_node, "ckil", 0); + hws[IMX6QDL_CLK_CKIH] = imx6q_obtain_fixed_clk_hw(ccm_node, "ckih1", 0); + hws[IMX6QDL_CLK_OSC] = imx6q_obtain_fixed_clk_hw(ccm_node, "osc", 0); + + /* Clock source from external clock via CLK1/2 PADs */ + hws[IMX6QDL_CLK_ANACLK1] = imx6q_obtain_fixed_clk_hw(ccm_node, "anaclk1", 0); + hws[IMX6QDL_CLK_ANACLK2] = imx6q_obtain_fixed_clk_hw(ccm_node, "anaclk2", 0); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); + anatop_base = base = of_iomap(np, 0); + WARN_ON(!base); + of_node_put(np); + + /* Audio/video PLL post dividers do not work on i.MX6q revision 1.0 */ + if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) { + post_div_table[1].div = 1; + post_div_table[2].div = 1; + video_div_table[1].div = 1; + video_div_table[3].div = 1; + } + + hws[IMX6QDL_PLL1_BYPASS_SRC] = imx_clk_hw_mux("pll1_bypass_src", base + 0x00, 14, 2, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6QDL_PLL2_BYPASS_SRC] = imx_clk_hw_mux("pll2_bypass_src", base + 0x30, 14, 2, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6QDL_PLL3_BYPASS_SRC] = imx_clk_hw_mux("pll3_bypass_src", base + 0x10, 14, 2, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6QDL_PLL4_BYPASS_SRC] = imx_clk_hw_mux("pll4_bypass_src", base + 0x70, 14, 2, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6QDL_PLL5_BYPASS_SRC] = imx_clk_hw_mux("pll5_bypass_src", base + 0xa0, 14, 2, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6QDL_PLL6_BYPASS_SRC] = imx_clk_hw_mux("pll6_bypass_src", base + 0xe0, 14, 2, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6QDL_PLL7_BYPASS_SRC] = imx_clk_hw_mux("pll7_bypass_src", base + 0x20, 14, 2, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + + /* type name parent_name base div_mask */ + hws[IMX6QDL_CLK_PLL1] = imx_clk_hw_pllv3(IMX_PLLV3_SYS, "pll1", "osc", base + 0x00, 0x7f); + hws[IMX6QDL_CLK_PLL2] = imx_clk_hw_pllv3(IMX_PLLV3_GENERIC, "pll2", "osc", base + 0x30, 0x1); + hws[IMX6QDL_CLK_PLL3] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll3", "osc", base + 0x10, 0x3); + hws[IMX6QDL_CLK_PLL4] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll4", "osc", base + 0x70, 0x7f); + hws[IMX6QDL_CLK_PLL5] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll5", "osc", base + 0xa0, 0x7f); + hws[IMX6QDL_CLK_PLL6] = imx_clk_hw_pllv3(IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3); + hws[IMX6QDL_CLK_PLL7] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll7", "osc", base + 0x20, 0x3); + + hws[IMX6QDL_PLL1_BYPASS] = imx_clk_hw_mux_flags("pll1_bypass", base + 0x00, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_PLL2_BYPASS] = imx_clk_hw_mux_flags("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_PLL3_BYPASS] = imx_clk_hw_mux_flags("pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_PLL4_BYPASS] = imx_clk_hw_mux_flags("pll4_bypass", base + 0x70, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_PLL6_BYPASS] = imx_clk_hw_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_PLL7_BYPASS] = imx_clk_hw_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); + + /* Do not bypass PLLs initially */ + clk_set_parent(hws[IMX6QDL_PLL1_BYPASS]->clk, hws[IMX6QDL_CLK_PLL1]->clk); + clk_set_parent(hws[IMX6QDL_PLL2_BYPASS]->clk, hws[IMX6QDL_CLK_PLL2]->clk); + clk_set_parent(hws[IMX6QDL_PLL3_BYPASS]->clk, hws[IMX6QDL_CLK_PLL3]->clk); + clk_set_parent(hws[IMX6QDL_PLL4_BYPASS]->clk, hws[IMX6QDL_CLK_PLL4]->clk); + clk_set_parent(hws[IMX6QDL_PLL5_BYPASS]->clk, hws[IMX6QDL_CLK_PLL5]->clk); + clk_set_parent(hws[IMX6QDL_PLL6_BYPASS]->clk, hws[IMX6QDL_CLK_PLL6]->clk); + clk_set_parent(hws[IMX6QDL_PLL7_BYPASS]->clk, hws[IMX6QDL_CLK_PLL7]->clk); + + hws[IMX6QDL_CLK_PLL1_SYS] = imx_clk_hw_gate("pll1_sys", "pll1_bypass", base + 0x00, 13); + hws[IMX6QDL_CLK_PLL2_BUS] = imx_clk_hw_gate("pll2_bus", "pll2_bypass", base + 0x30, 13); + hws[IMX6QDL_CLK_PLL3_USB_OTG] = imx_clk_hw_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13); + hws[IMX6QDL_CLK_PLL4_AUDIO] = imx_clk_hw_gate("pll4_audio", "pll4_bypass", base + 0x70, 13); + hws[IMX6QDL_CLK_PLL5_VIDEO] = imx_clk_hw_gate("pll5_video", "pll5_bypass", base + 0xa0, 13); + hws[IMX6QDL_CLK_PLL6_ENET] = imx_clk_hw_gate("pll6_enet", "pll6_bypass", base + 0xe0, 13); + hws[IMX6QDL_CLK_PLL7_USB_HOST] = imx_clk_hw_gate("pll7_usb_host", "pll7_bypass", base + 0x20, 13); + + /* + * Bit 20 is the reserved and read-only bit, we do this only for: + * - Do nothing for usbphy clk_enable/disable + * - Keep refcount when do usbphy clk_enable/disable, in that case, + * the clk framework may need to enable/disable usbphy's parent + */ + hws[IMX6QDL_CLK_USBPHY1] = imx_clk_hw_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + hws[IMX6QDL_CLK_USBPHY2] = imx_clk_hw_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + + /* + * usbphy*_gate needs to be on after system boots up, and software + * never needs to control it anymore. + */ + hws[IMX6QDL_CLK_USBPHY1_GATE] = imx_clk_hw_gate("usbphy1_gate", "dummy", base + 0x10, 6); + hws[IMX6QDL_CLK_USBPHY2_GATE] = imx_clk_hw_gate("usbphy2_gate", "dummy", base + 0x20, 6); + + /* + * The ENET PLL is special in that is has multiple outputs with + * different post-dividers that are all affected by the single bypass + * bit, so a single mux bit affects 3 independent branches of the clock + * tree. There is no good way to model this in the clock framework and + * dynamically changing the bypass bit, will yield unexpected results. + * So we treat any configuration that bypasses the ENET PLL as + * essentially static with the divider ratios reflecting the bypass + * status. + * + */ + if (!pll6_bypassed(ccm_node)) { + hws[IMX6QDL_CLK_SATA_REF] = imx_clk_hw_fixed_factor("sata_ref", "pll6_enet", 1, 5); + hws[IMX6QDL_CLK_PCIE_REF] = imx_clk_hw_fixed_factor("pcie_ref", "pll6_enet", 1, 4); + hws[IMX6QDL_CLK_ENET_REF] = clk_hw_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + base + 0xe0, 0, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + } else { + hws[IMX6QDL_CLK_SATA_REF] = imx_clk_hw_fixed_factor("sata_ref", "pll6_enet", 1, 1); + hws[IMX6QDL_CLK_PCIE_REF] = imx_clk_hw_fixed_factor("pcie_ref", "pll6_enet", 1, 1); + hws[IMX6QDL_CLK_ENET_REF] = imx_clk_hw_fixed_factor("enet_ref", "pll6_enet", 1, 1); + } + + hws[IMX6QDL_CLK_SATA_REF_100M] = imx_clk_hw_gate("sata_ref_100m", "sata_ref", base + 0xe0, 20); + hws[IMX6QDL_CLK_PCIE_REF_125M] = imx_clk_hw_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); + + hws[IMX6QDL_CLK_LVDS1_SEL] = imx_clk_hw_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + hws[IMX6QDL_CLK_LVDS2_SEL] = imx_clk_hw_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + + /* + * lvds1_gate and lvds2_gate are pseudo-gates. Both can be + * independently configured as clock inputs or outputs. We treat + * the "output_enable" bit as a gate, even though it's really just + * enabling clock output. Initially the gate bits are cleared, as + * otherwise the exclusive configuration gets locked in the setup done + * by software running before the clock driver, with no way to change + * it. + */ + writel(readl(base + 0x160) & ~0x3c00, base + 0x160); + hws[IMX6QDL_CLK_LVDS1_GATE] = imx_clk_hw_gate_exclusive("lvds1_gate", "lvds1_sel", base + 0x160, 10, BIT(12)); + hws[IMX6QDL_CLK_LVDS2_GATE] = imx_clk_hw_gate_exclusive("lvds2_gate", "lvds2_sel", base + 0x160, 11, BIT(13)); + + hws[IMX6QDL_CLK_LVDS1_IN] = imx_clk_hw_gate_exclusive("lvds1_in", "anaclk1", base + 0x160, 12, BIT(10)); + hws[IMX6QDL_CLK_LVDS2_IN] = imx_clk_hw_gate_exclusive("lvds2_in", "anaclk2", base + 0x160, 13, BIT(11)); + + /* name parent_name reg idx */ + hws[IMX6QDL_CLK_PLL2_PFD0_352M] = imx_clk_hw_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); + hws[IMX6QDL_CLK_PLL2_PFD1_594M] = imx_clk_hw_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); + hws[IMX6QDL_CLK_PLL2_PFD2_396M] = imx_clk_hw_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); + hws[IMX6QDL_CLK_PLL3_PFD0_720M] = imx_clk_hw_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); + hws[IMX6QDL_CLK_PLL3_PFD1_540M] = imx_clk_hw_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); + hws[IMX6QDL_CLK_PLL3_PFD2_508M] = imx_clk_hw_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); + hws[IMX6QDL_CLK_PLL3_PFD3_454M] = imx_clk_hw_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); + + /* name parent_name mult div */ + hws[IMX6QDL_CLK_PLL2_198M] = imx_clk_hw_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); + hws[IMX6QDL_CLK_PLL3_120M] = imx_clk_hw_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + hws[IMX6QDL_CLK_PLL3_80M] = imx_clk_hw_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + hws[IMX6QDL_CLK_PLL3_60M] = imx_clk_hw_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + hws[IMX6QDL_CLK_TWD] = imx_clk_hw_fixed_factor("twd", "arm", 1, 2); + hws[IMX6QDL_CLK_GPT_3M] = imx_clk_hw_fixed_factor("gpt_3m", "osc", 1, 8); + hws[IMX6QDL_CLK_VIDEO_27M] = imx_clk_hw_fixed_factor("video_27m", "pll3_pfd1_540m", 1, 20); + if (clk_on_imx6dl() || clk_on_imx6qp()) { + hws[IMX6QDL_CLK_GPU2D_AXI] = imx_clk_hw_fixed_factor("gpu2d_axi", "mmdc_ch0_axi_podf", 1, 1); + hws[IMX6QDL_CLK_GPU3D_AXI] = imx_clk_hw_fixed_factor("gpu3d_axi", "mmdc_ch0_axi_podf", 1, 1); + } + + hws[IMX6QDL_CLK_PLL4_POST_DIV] = clk_hw_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + if (clk_on_imx6q() || clk_on_imx6qp()) + hws[IMX6QDL_CLK_PLL4_AUDIO_DIV] = imx_clk_hw_fixed_factor("pll4_audio_div", "pll4_post_div", 1, 1); + else + hws[IMX6QDL_CLK_PLL4_AUDIO_DIV] = clk_hw_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); + hws[IMX6QDL_CLK_PLL5_POST_DIV] = clk_hw_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6QDL_CLK_PLL5_VIDEO_DIV] = clk_hw_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + + /* name reg shift width parent_names num_parents */ + hws[IMX6QDL_CLK_STEP] = imx_clk_hw_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + hws[IMX6QDL_CLK_PLL1_SW] = imx_clk_hw_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); + hws[IMX6QDL_CLK_PERIPH_PRE] = imx_clk_hw_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + hws[IMX6QDL_CLK_PERIPH2_PRE] = imx_clk_hw_mux("periph2_pre", base + 0x18, 21, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + hws[IMX6QDL_CLK_PERIPH_CLK2_SEL] = imx_clk_hw_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + hws[IMX6QDL_CLK_PERIPH2_CLK2_SEL] = imx_clk_hw_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + hws[IMX6QDL_CLK_AXI_SEL] = imx_clk_hw_mux("axi_sel", base + 0x14, 6, 2, axi_sels, ARRAY_SIZE(axi_sels)); + hws[IMX6QDL_CLK_ESAI_SEL] = imx_clk_hw_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6QDL_CLK_ASRC_SEL] = imx_clk_hw_mux("asrc_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6QDL_CLK_SPDIF_SEL] = imx_clk_hw_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + if (clk_on_imx6q()) { + hws[IMX6QDL_CLK_GPU2D_AXI] = imx_clk_hw_mux("gpu2d_axi", base + 0x18, 0, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); + hws[IMX6QDL_CLK_GPU3D_AXI] = imx_clk_hw_mux("gpu3d_axi", base + 0x18, 1, 1, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); + } + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_CAN_SEL] = imx_clk_hw_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); + hws[IMX6QDL_CLK_ECSPI_SEL] = imx_clk_hw_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + hws[IMX6QDL_CLK_IPG_PER_SEL] = imx_clk_hw_mux("ipg_per_sel", base + 0x1c, 6, 1, ipg_per_sels, ARRAY_SIZE(ipg_per_sels)); + hws[IMX6QDL_CLK_UART_SEL] = imx_clk_hw_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + hws[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_hw_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels_2, ARRAY_SIZE(gpu2d_core_sels_2)); + } else if (clk_on_imx6dl()) { + hws[IMX6QDL_CLK_MLB_SEL] = imx_clk_hw_mux("mlb_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels)); + } else { + hws[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_hw_mux("gpu2d_core_sel", base + 0x18, 16, 2, gpu2d_core_sels, ARRAY_SIZE(gpu2d_core_sels)); + } + hws[IMX6QDL_CLK_GPU3D_CORE_SEL] = imx_clk_hw_mux("gpu3d_core_sel", base + 0x18, 4, 2, gpu3d_core_sels, ARRAY_SIZE(gpu3d_core_sels)); + if (clk_on_imx6dl()) + hws[IMX6QDL_CLK_GPU2D_CORE_SEL] = imx_clk_hw_mux("gpu2d_core_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); + else + hws[IMX6QDL_CLK_GPU3D_SHADER_SEL] = imx_clk_hw_mux("gpu3d_shader_sel", base + 0x18, 8, 2, gpu3d_shader_sels, ARRAY_SIZE(gpu3d_shader_sels)); + hws[IMX6QDL_CLK_IPU1_SEL] = imx_clk_hw_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); + hws[IMX6QDL_CLK_IPU2_SEL] = imx_clk_hw_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); + + disable_anatop_clocks(anatop_base); + + imx_mmdc_mask_handshake(base, 1); + + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_hw_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_hw_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); + } else { + /* + * The LDB_DI0/1_SEL muxes are registered read-only due to a hardware + * bug. Set the muxes to the requested values before registering the + * ldb_di_sel clocks. + */ + init_ldb_clks(np, base); + + hws[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_hw_mux_ldb("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); + hws[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_hw_mux_ldb("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels)); + } + + hws[IMX6QDL_CLK_IPU1_DI0_PRE_SEL] = imx_clk_hw_mux_flags("ipu1_di0_pre_sel", base + 0x34, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU1_DI1_PRE_SEL] = imx_clk_hw_mux_flags("ipu1_di1_pre_sel", base + 0x34, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU2_DI0_PRE_SEL] = imx_clk_hw_mux_flags("ipu2_di0_pre_sel", base + 0x38, 6, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU2_DI1_PRE_SEL] = imx_clk_hw_mux_flags("ipu2_di1_pre_sel", base + 0x38, 15, 3, ipu_di_pre_sels, ARRAY_SIZE(ipu_di_pre_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_HSI_TX_SEL] = imx_clk_hw_mux("hsi_tx_sel", base + 0x30, 28, 1, hsi_tx_sels, ARRAY_SIZE(hsi_tx_sels)); + hws[IMX6QDL_CLK_PCIE_AXI_SEL] = imx_clk_hw_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); + + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_IPU1_DI0_SEL] = imx_clk_hw_mux_flags("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels_2, ARRAY_SIZE(ipu1_di0_sels_2), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU1_DI1_SEL] = imx_clk_hw_mux_flags("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels_2, ARRAY_SIZE(ipu1_di1_sels_2), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU2_DI0_SEL] = imx_clk_hw_mux_flags("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels_2, ARRAY_SIZE(ipu2_di0_sels_2), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU2_DI1_SEL] = imx_clk_hw_mux_flags("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels_2, ARRAY_SIZE(ipu2_di1_sels_2), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_SSI1_SEL] = imx_clk_hw_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6QDL_CLK_SSI2_SEL] = imx_clk_hw_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6QDL_CLK_SSI3_SEL] = imx_clk_hw_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6QDL_CLK_USDHC1_SEL] = imx_clk_hw_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6QDL_CLK_USDHC2_SEL] = imx_clk_hw_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6QDL_CLK_USDHC3_SEL] = imx_clk_hw_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6QDL_CLK_USDHC4_SEL] = imx_clk_hw_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6QDL_CLK_ENFC_SEL] = imx_clk_hw_mux("enfc_sel", base + 0x2c, 15, 3, enfc_sels_2, ARRAY_SIZE(enfc_sels_2)); + hws[IMX6QDL_CLK_EIM_SEL] = imx_clk_hw_mux("eim_sel", base + 0x1c, 27, 2, eim_sels, ARRAY_SIZE(eim_sels)); + hws[IMX6QDL_CLK_EIM_SLOW_SEL] = imx_clk_hw_mux("eim_slow_sel", base + 0x1c, 29, 2, eim_slow_sels, ARRAY_SIZE(eim_slow_sels)); + hws[IMX6QDL_CLK_PRE_AXI] = imx_clk_hw_mux("pre_axi", base + 0x18, 1, 1, pre_axi_sels, ARRAY_SIZE(pre_axi_sels)); + } else { + hws[IMX6QDL_CLK_IPU1_DI0_SEL] = imx_clk_hw_mux_flags("ipu1_di0_sel", base + 0x34, 0, 3, ipu1_di0_sels, ARRAY_SIZE(ipu1_di0_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU1_DI1_SEL] = imx_clk_hw_mux_flags("ipu1_di1_sel", base + 0x34, 9, 3, ipu1_di1_sels, ARRAY_SIZE(ipu1_di1_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU2_DI0_SEL] = imx_clk_hw_mux_flags("ipu2_di0_sel", base + 0x38, 0, 3, ipu2_di0_sels, ARRAY_SIZE(ipu2_di0_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_IPU2_DI1_SEL] = imx_clk_hw_mux_flags("ipu2_di1_sel", base + 0x38, 9, 3, ipu2_di1_sels, ARRAY_SIZE(ipu2_di1_sels), CLK_SET_RATE_PARENT); + hws[IMX6QDL_CLK_SSI1_SEL] = imx_clk_hw_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_SSI2_SEL] = imx_clk_hw_fixup_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_SSI3_SEL] = imx_clk_hw_fixup_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_USDHC1_SEL] = imx_clk_hw_fixup_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_USDHC2_SEL] = imx_clk_hw_fixup_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_USDHC3_SEL] = imx_clk_hw_fixup_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_USDHC4_SEL] = imx_clk_hw_fixup_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_ENFC_SEL] = imx_clk_hw_mux("enfc_sel", base + 0x2c, 16, 2, enfc_sels, ARRAY_SIZE(enfc_sels)); + hws[IMX6QDL_CLK_EIM_SEL] = imx_clk_hw_fixup_mux("eim_sel", base + 0x1c, 27, 2, eim_sels, ARRAY_SIZE(eim_sels), imx_cscmr1_fixup); + hws[IMX6QDL_CLK_EIM_SLOW_SEL] = imx_clk_hw_fixup_mux("eim_slow_sel", base + 0x1c, 29, 2, eim_slow_sels, ARRAY_SIZE(eim_slow_sels), imx_cscmr1_fixup); + } + + hws[IMX6QDL_CLK_VDO_AXI_SEL] = imx_clk_hw_mux("vdo_axi_sel", base + 0x18, 11, 1, vdo_axi_sels, ARRAY_SIZE(vdo_axi_sels)); + hws[IMX6QDL_CLK_VPU_AXI_SEL] = imx_clk_hw_mux("vpu_axi_sel", base + 0x18, 14, 2, vpu_axi_sels, ARRAY_SIZE(vpu_axi_sels)); + hws[IMX6QDL_CLK_CKO1_SEL] = imx_clk_hw_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); + hws[IMX6QDL_CLK_CKO2_SEL] = imx_clk_hw_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); + hws[IMX6QDL_CLK_CKO] = imx_clk_hw_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); + + /* name reg shift width busy: reg, shift parent_names num_parents */ + hws[IMX6QDL_CLK_PERIPH] = imx_clk_hw_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + hws[IMX6QDL_CLK_PERIPH2] = imx_clk_hw_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + + /* name parent_name reg shift width */ + hws[IMX6QDL_CLK_PERIPH_CLK2] = imx_clk_hw_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); + hws[IMX6QDL_CLK_PERIPH2_CLK2] = imx_clk_hw_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + hws[IMX6QDL_CLK_IPG] = imx_clk_hw_divider("ipg", "ahb", base + 0x14, 8, 2); + hws[IMX6QDL_CLK_ESAI_PRED] = imx_clk_hw_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); + hws[IMX6QDL_CLK_ESAI_PODF] = imx_clk_hw_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); + hws[IMX6QDL_CLK_ASRC_PRED] = imx_clk_hw_divider("asrc_pred", "asrc_sel", base + 0x30, 12, 3); + hws[IMX6QDL_CLK_ASRC_PODF] = imx_clk_hw_divider("asrc_podf", "asrc_pred", base + 0x30, 9, 3); + hws[IMX6QDL_CLK_SPDIF_PRED] = imx_clk_hw_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); + hws[IMX6QDL_CLK_SPDIF_PODF] = imx_clk_hw_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); + + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_IPG_PER] = imx_clk_hw_divider("ipg_per", "ipg_per_sel", base + 0x1c, 0, 6); + hws[IMX6QDL_CLK_ECSPI_ROOT] = imx_clk_hw_divider("ecspi_root", "ecspi_sel", base + 0x38, 19, 6); + hws[IMX6QDL_CLK_CAN_ROOT] = imx_clk_hw_divider("can_root", "can_sel", base + 0x20, 2, 6); + hws[IMX6QDL_CLK_UART_SERIAL_PODF] = imx_clk_hw_divider("uart_serial_podf", "uart_sel", base + 0x24, 0, 6); + hws[IMX6QDL_CLK_LDB_DI0_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di0_div_3_5", "ldb_di0", 2, 7); + hws[IMX6QDL_CLK_LDB_DI1_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di1_div_3_5", "ldb_di1", 2, 7); + } else { + hws[IMX6QDL_CLK_ECSPI_ROOT] = imx_clk_hw_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6); + hws[IMX6QDL_CLK_CAN_ROOT] = imx_clk_hw_divider("can_root", "pll3_60m", base + 0x20, 2, 6); + hws[IMX6QDL_CLK_IPG_PER] = imx_clk_hw_fixup_divider("ipg_per", "ipg", base + 0x1c, 0, 6, imx_cscmr1_fixup); + hws[IMX6QDL_CLK_UART_SERIAL_PODF] = imx_clk_hw_divider("uart_serial_podf", "pll3_80m", base + 0x24, 0, 6); + hws[IMX6QDL_CLK_LDB_DI0_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); + hws[IMX6QDL_CLK_LDB_DI1_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); + } + + if (clk_on_imx6dl()) + hws[IMX6QDL_CLK_MLB_PODF] = imx_clk_hw_divider("mlb_podf", "mlb_sel", base + 0x18, 23, 3); + else + hws[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_hw_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 23, 3); + hws[IMX6QDL_CLK_GPU3D_CORE_PODF] = imx_clk_hw_divider("gpu3d_core_podf", "gpu3d_core_sel", base + 0x18, 26, 3); + if (clk_on_imx6dl()) + hws[IMX6QDL_CLK_GPU2D_CORE_PODF] = imx_clk_hw_divider("gpu2d_core_podf", "gpu2d_core_sel", base + 0x18, 29, 3); + else + hws[IMX6QDL_CLK_GPU3D_SHADER] = imx_clk_hw_divider("gpu3d_shader", "gpu3d_shader_sel", base + 0x18, 29, 3); + hws[IMX6QDL_CLK_IPU1_PODF] = imx_clk_hw_divider("ipu1_podf", "ipu1_sel", base + 0x3c, 11, 3); + hws[IMX6QDL_CLK_IPU2_PODF] = imx_clk_hw_divider("ipu2_podf", "ipu2_sel", base + 0x3c, 16, 3); + hws[IMX6QDL_CLK_LDB_DI0_PODF] = imx_clk_hw_divider_flags("ldb_di0_podf", "ldb_di0_div_3_5", base + 0x20, 10, 1, 0); + hws[IMX6QDL_CLK_LDB_DI1_PODF] = imx_clk_hw_divider_flags("ldb_di1_podf", "ldb_di1_div_3_5", base + 0x20, 11, 1, 0); + hws[IMX6QDL_CLK_IPU1_DI0_PRE] = imx_clk_hw_divider("ipu1_di0_pre", "ipu1_di0_pre_sel", base + 0x34, 3, 3); + hws[IMX6QDL_CLK_IPU1_DI1_PRE] = imx_clk_hw_divider("ipu1_di1_pre", "ipu1_di1_pre_sel", base + 0x34, 12, 3); + hws[IMX6QDL_CLK_IPU2_DI0_PRE] = imx_clk_hw_divider("ipu2_di0_pre", "ipu2_di0_pre_sel", base + 0x38, 3, 3); + hws[IMX6QDL_CLK_IPU2_DI1_PRE] = imx_clk_hw_divider("ipu2_di1_pre", "ipu2_di1_pre_sel", base + 0x38, 12, 3); + hws[IMX6QDL_CLK_HSI_TX_PODF] = imx_clk_hw_divider("hsi_tx_podf", "hsi_tx_sel", base + 0x30, 29, 3); + hws[IMX6QDL_CLK_SSI1_PRED] = imx_clk_hw_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + hws[IMX6QDL_CLK_SSI1_PODF] = imx_clk_hw_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + hws[IMX6QDL_CLK_SSI2_PRED] = imx_clk_hw_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + hws[IMX6QDL_CLK_SSI2_PODF] = imx_clk_hw_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + hws[IMX6QDL_CLK_SSI3_PRED] = imx_clk_hw_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + hws[IMX6QDL_CLK_SSI3_PODF] = imx_clk_hw_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + hws[IMX6QDL_CLK_USDHC1_PODF] = imx_clk_hw_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + hws[IMX6QDL_CLK_USDHC2_PODF] = imx_clk_hw_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + hws[IMX6QDL_CLK_USDHC3_PODF] = imx_clk_hw_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + hws[IMX6QDL_CLK_USDHC4_PODF] = imx_clk_hw_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); + hws[IMX6QDL_CLK_ENFC_PRED] = imx_clk_hw_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); + hws[IMX6QDL_CLK_ENFC_PODF] = imx_clk_hw_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_EIM_PODF] = imx_clk_hw_divider("eim_podf", "eim_sel", base + 0x1c, 20, 3); + hws[IMX6QDL_CLK_EIM_SLOW_PODF] = imx_clk_hw_divider("eim_slow_podf", "eim_slow_sel", base + 0x1c, 23, 3); + } else { + hws[IMX6QDL_CLK_EIM_PODF] = imx_clk_hw_fixup_divider("eim_podf", "eim_sel", base + 0x1c, 20, 3, imx_cscmr1_fixup); + hws[IMX6QDL_CLK_EIM_SLOW_PODF] = imx_clk_hw_fixup_divider("eim_slow_podf", "eim_slow_sel", base + 0x1c, 23, 3, imx_cscmr1_fixup); + } + + hws[IMX6QDL_CLK_VPU_AXI_PODF] = imx_clk_hw_divider("vpu_axi_podf", "vpu_axi_sel", base + 0x24, 25, 3); + hws[IMX6QDL_CLK_CKO1_PODF] = imx_clk_hw_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); + hws[IMX6QDL_CLK_CKO2_PODF] = imx_clk_hw_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); + + /* name parent_name reg shift width busy: reg, shift */ + hws[IMX6QDL_CLK_AXI] = imx_clk_hw_busy_divider("axi", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0); + hws[IMX6QDL_CLK_MMDC_CH0_AXI_PODF] = imx_clk_hw_busy_divider("mmdc_ch0_axi_podf", "periph", base + 0x14, 19, 3, base + 0x48, 4); + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_MMDC_CH1_AXI_CG] = imx_clk_hw_gate("mmdc_ch1_axi_cg", "periph2", base + 0x4, 18); + hws[IMX6QDL_CLK_MMDC_CH1_AXI_PODF] = imx_clk_hw_busy_divider("mmdc_ch1_axi_podf", "mmdc_ch1_axi_cg", base + 0x14, 3, 3, base + 0x48, 2); + } else { + hws[IMX6QDL_CLK_MMDC_CH1_AXI_PODF] = imx_clk_hw_busy_divider("mmdc_ch1_axi_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + } + hws[IMX6QDL_CLK_ARM] = imx_clk_hw_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + hws[IMX6QDL_CLK_AHB] = imx_clk_hw_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + + /* name parent_name reg shift */ + hws[IMX6QDL_CLK_APBH_DMA] = imx_clk_hw_gate2("apbh_dma", "usdhc3", base + 0x68, 4); + hws[IMX6QDL_CLK_ASRC] = imx_clk_hw_gate2_shared("asrc", "asrc_podf", base + 0x68, 6, &share_count_asrc); + hws[IMX6QDL_CLK_ASRC_IPG] = imx_clk_hw_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); + hws[IMX6QDL_CLK_ASRC_MEM] = imx_clk_hw_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); + hws[IMX6QDL_CLK_CAAM_MEM] = imx_clk_hw_gate2("caam_mem", "ahb", base + 0x68, 8); + hws[IMX6QDL_CLK_CAAM_ACLK] = imx_clk_hw_gate2("caam_aclk", "ahb", base + 0x68, 10); + hws[IMX6QDL_CLK_CAAM_IPG] = imx_clk_hw_gate2("caam_ipg", "ipg", base + 0x68, 12); + hws[IMX6QDL_CLK_CAN1_IPG] = imx_clk_hw_gate2("can1_ipg", "ipg", base + 0x68, 14); + hws[IMX6QDL_CLK_CAN1_SERIAL] = imx_clk_hw_gate2("can1_serial", "can_root", base + 0x68, 16); + hws[IMX6QDL_CLK_CAN2_IPG] = imx_clk_hw_gate2("can2_ipg", "ipg", base + 0x68, 18); + hws[IMX6QDL_CLK_CAN2_SERIAL] = imx_clk_hw_gate2("can2_serial", "can_root", base + 0x68, 20); + hws[IMX6QDL_CLK_DCIC1] = imx_clk_hw_gate2("dcic1", "ipu1_podf", base + 0x68, 24); + hws[IMX6QDL_CLK_DCIC2] = imx_clk_hw_gate2("dcic2", "ipu2_podf", base + 0x68, 26); + hws[IMX6QDL_CLK_ECSPI1] = imx_clk_hw_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); + hws[IMX6QDL_CLK_ECSPI2] = imx_clk_hw_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); + hws[IMX6QDL_CLK_ECSPI3] = imx_clk_hw_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); + hws[IMX6QDL_CLK_ECSPI4] = imx_clk_hw_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); + if (clk_on_imx6dl()) + hws[IMX6DL_CLK_I2C4] = imx_clk_hw_gate2("i2c4", "ipg_per", base + 0x6c, 8); + else + hws[IMX6Q_CLK_ECSPI5] = imx_clk_hw_gate2("ecspi5", "ecspi_root", base + 0x6c, 8); + hws[IMX6QDL_CLK_ENET] = imx_clk_hw_gate2("enet", "ipg", base + 0x6c, 10); + hws[IMX6QDL_CLK_EPIT1] = imx_clk_hw_gate2("epit1", "ipg", base + 0x6c, 12); + hws[IMX6QDL_CLK_EPIT2] = imx_clk_hw_gate2("epit2", "ipg", base + 0x6c, 14); + hws[IMX6QDL_CLK_ESAI_EXTAL] = imx_clk_hw_gate2_shared("esai_extal", "esai_podf", base + 0x6c, 16, &share_count_esai); + hws[IMX6QDL_CLK_ESAI_IPG] = imx_clk_hw_gate2_shared("esai_ipg", "ahb", base + 0x6c, 16, &share_count_esai); + hws[IMX6QDL_CLK_ESAI_MEM] = imx_clk_hw_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai); + hws[IMX6QDL_CLK_GPT_IPG] = imx_clk_hw_gate2("gpt_ipg", "ipg", base + 0x6c, 20); + hws[IMX6QDL_CLK_GPT_IPG_PER] = imx_clk_hw_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22); + hws[IMX6QDL_CLK_GPU2D_CORE] = imx_clk_hw_gate2("gpu2d_core", "gpu2d_core_podf", base + 0x6c, 24); + hws[IMX6QDL_CLK_GPU3D_CORE] = imx_clk_hw_gate2("gpu3d_core", "gpu3d_core_podf", base + 0x6c, 26); + hws[IMX6QDL_CLK_HDMI_IAHB] = imx_clk_hw_gate2("hdmi_iahb", "ahb", base + 0x70, 0); + hws[IMX6QDL_CLK_HDMI_ISFR] = imx_clk_hw_gate2("hdmi_isfr", "mipi_core_cfg", base + 0x70, 4); + hws[IMX6QDL_CLK_I2C1] = imx_clk_hw_gate2("i2c1", "ipg_per", base + 0x70, 6); + hws[IMX6QDL_CLK_I2C2] = imx_clk_hw_gate2("i2c2", "ipg_per", base + 0x70, 8); + hws[IMX6QDL_CLK_I2C3] = imx_clk_hw_gate2("i2c3", "ipg_per", base + 0x70, 10); + hws[IMX6QDL_CLK_IIM] = imx_clk_hw_gate2("iim", "ipg", base + 0x70, 12); + hws[IMX6QDL_CLK_ENFC] = imx_clk_hw_gate2("enfc", "enfc_podf", base + 0x70, 14); + hws[IMX6QDL_CLK_VDOA] = imx_clk_hw_gate2("vdoa", "vdo_axi", base + 0x70, 26); + hws[IMX6QDL_CLK_IPU1] = imx_clk_hw_gate2("ipu1", "ipu1_podf", base + 0x74, 0); + hws[IMX6QDL_CLK_IPU1_DI0] = imx_clk_hw_gate2("ipu1_di0", "ipu1_di0_sel", base + 0x74, 2); + hws[IMX6QDL_CLK_IPU1_DI1] = imx_clk_hw_gate2("ipu1_di1", "ipu1_di1_sel", base + 0x74, 4); + hws[IMX6QDL_CLK_IPU2] = imx_clk_hw_gate2("ipu2", "ipu2_podf", base + 0x74, 6); + hws[IMX6QDL_CLK_IPU2_DI0] = imx_clk_hw_gate2("ipu2_di0", "ipu2_di0_sel", base + 0x74, 8); + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_LDB_DI0] = imx_clk_hw_gate2("ldb_di0", "ldb_di0_sel", base + 0x74, 12); + hws[IMX6QDL_CLK_LDB_DI1] = imx_clk_hw_gate2("ldb_di1", "ldb_di1_sel", base + 0x74, 14); + } else { + hws[IMX6QDL_CLK_LDB_DI0] = imx_clk_hw_gate2("ldb_di0", "ldb_di0_podf", base + 0x74, 12); + hws[IMX6QDL_CLK_LDB_DI1] = imx_clk_hw_gate2("ldb_di1", "ldb_di1_podf", base + 0x74, 14); + } + hws[IMX6QDL_CLK_IPU2_DI1] = imx_clk_hw_gate2("ipu2_di1", "ipu2_di1_sel", base + 0x74, 10); + hws[IMX6QDL_CLK_HSI_TX] = imx_clk_hw_gate2_shared("hsi_tx", "hsi_tx_podf", base + 0x74, 16, &share_count_mipi_core_cfg); + hws[IMX6QDL_CLK_MIPI_CORE_CFG] = imx_clk_hw_gate2_shared("mipi_core_cfg", "video_27m", base + 0x74, 16, &share_count_mipi_core_cfg); + hws[IMX6QDL_CLK_MIPI_IPG] = imx_clk_hw_gate2_shared("mipi_ipg", "ipg", base + 0x74, 16, &share_count_mipi_core_cfg); + + if (clk_on_imx6dl()) + /* + * The multiplexer and divider of the imx6q clock gpu2d get + * redefined/reused as mlb_sys_sel and mlb_sys_clk_podf on imx6dl. + */ + hws[IMX6QDL_CLK_MLB] = imx_clk_hw_gate2("mlb", "mlb_podf", base + 0x74, 18); + else + hws[IMX6QDL_CLK_MLB] = imx_clk_hw_gate2("mlb", "axi", base + 0x74, 18); + hws[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_hw_gate2_flags("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20, CLK_IS_CRITICAL); + hws[IMX6QDL_CLK_MMDC_CH1_AXI] = imx_clk_hw_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); + hws[IMX6QDL_CLK_MMDC_P0_IPG] = imx_clk_hw_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + hws[IMX6QDL_CLK_OCRAM] = imx_clk_hw_gate2("ocram", "ahb", base + 0x74, 28); + hws[IMX6QDL_CLK_OPENVG_AXI] = imx_clk_hw_gate2("openvg_axi", "axi", base + 0x74, 30); + hws[IMX6QDL_CLK_PCIE_AXI] = imx_clk_hw_gate2("pcie_axi", "pcie_axi_sel", base + 0x78, 0); + hws[IMX6QDL_CLK_PER1_BCH] = imx_clk_hw_gate2("per1_bch", "usdhc3", base + 0x78, 12); + hws[IMX6QDL_CLK_PWM1] = imx_clk_hw_gate2("pwm1", "ipg_per", base + 0x78, 16); + hws[IMX6QDL_CLK_PWM2] = imx_clk_hw_gate2("pwm2", "ipg_per", base + 0x78, 18); + hws[IMX6QDL_CLK_PWM3] = imx_clk_hw_gate2("pwm3", "ipg_per", base + 0x78, 20); + hws[IMX6QDL_CLK_PWM4] = imx_clk_hw_gate2("pwm4", "ipg_per", base + 0x78, 22); + hws[IMX6QDL_CLK_GPMI_BCH_APB] = imx_clk_hw_gate2("gpmi_bch_apb", "usdhc3", base + 0x78, 24); + hws[IMX6QDL_CLK_GPMI_BCH] = imx_clk_hw_gate2("gpmi_bch", "usdhc4", base + 0x78, 26); + hws[IMX6QDL_CLK_GPMI_IO] = imx_clk_hw_gate2("gpmi_io", "enfc", base + 0x78, 28); + hws[IMX6QDL_CLK_GPMI_APB] = imx_clk_hw_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); + hws[IMX6QDL_CLK_ROM] = imx_clk_hw_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL); + hws[IMX6QDL_CLK_SATA] = imx_clk_hw_gate2("sata", "ahb", base + 0x7c, 4); + hws[IMX6QDL_CLK_SDMA] = imx_clk_hw_gate2("sdma", "ahb", base + 0x7c, 6); + hws[IMX6QDL_CLK_SPBA] = imx_clk_hw_gate2("spba", "ipg", base + 0x7c, 12); + hws[IMX6QDL_CLK_SPDIF] = imx_clk_hw_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_spdif); + hws[IMX6QDL_CLK_SPDIF_GCLK] = imx_clk_hw_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_spdif); + hws[IMX6QDL_CLK_SSI1_IPG] = imx_clk_hw_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6QDL_CLK_SSI2_IPG] = imx_clk_hw_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6QDL_CLK_SSI3_IPG] = imx_clk_hw_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6QDL_CLK_SSI1] = imx_clk_hw_gate2_shared("ssi1", "ssi1_podf", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6QDL_CLK_SSI2] = imx_clk_hw_gate2_shared("ssi2", "ssi2_podf", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6QDL_CLK_SSI3] = imx_clk_hw_gate2_shared("ssi3", "ssi3_podf", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6QDL_CLK_UART_IPG] = imx_clk_hw_gate2("uart_ipg", "ipg", base + 0x7c, 24); + hws[IMX6QDL_CLK_UART_SERIAL] = imx_clk_hw_gate2("uart_serial", "uart_serial_podf", base + 0x7c, 26); + hws[IMX6QDL_CLK_USBOH3] = imx_clk_hw_gate2("usboh3", "ipg", base + 0x80, 0); + hws[IMX6QDL_CLK_USDHC1] = imx_clk_hw_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + hws[IMX6QDL_CLK_USDHC2] = imx_clk_hw_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + hws[IMX6QDL_CLK_USDHC3] = imx_clk_hw_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + hws[IMX6QDL_CLK_USDHC4] = imx_clk_hw_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + hws[IMX6QDL_CLK_EIM_SLOW] = imx_clk_hw_gate2("eim_slow", "eim_slow_podf", base + 0x80, 10); + hws[IMX6QDL_CLK_VDO_AXI] = imx_clk_hw_gate2("vdo_axi", "vdo_axi_sel", base + 0x80, 12); + hws[IMX6QDL_CLK_VPU_AXI] = imx_clk_hw_gate2("vpu_axi", "vpu_axi_podf", base + 0x80, 14); + if (clk_on_imx6qp()) { + hws[IMX6QDL_CLK_PRE0] = imx_clk_hw_gate2("pre0", "pre_axi", base + 0x80, 16); + hws[IMX6QDL_CLK_PRE1] = imx_clk_hw_gate2("pre1", "pre_axi", base + 0x80, 18); + hws[IMX6QDL_CLK_PRE2] = imx_clk_hw_gate2("pre2", "pre_axi", base + 0x80, 20); + hws[IMX6QDL_CLK_PRE3] = imx_clk_hw_gate2("pre3", "pre_axi", base + 0x80, 22); + hws[IMX6QDL_CLK_PRG0_AXI] = imx_clk_hw_gate2_shared("prg0_axi", "ipu1_podf", base + 0x80, 24, &share_count_prg0); + hws[IMX6QDL_CLK_PRG1_AXI] = imx_clk_hw_gate2_shared("prg1_axi", "ipu2_podf", base + 0x80, 26, &share_count_prg1); + hws[IMX6QDL_CLK_PRG0_APB] = imx_clk_hw_gate2_shared("prg0_apb", "ipg", base + 0x80, 24, &share_count_prg0); + hws[IMX6QDL_CLK_PRG1_APB] = imx_clk_hw_gate2_shared("prg1_apb", "ipg", base + 0x80, 26, &share_count_prg1); + } + hws[IMX6QDL_CLK_CKO1] = imx_clk_hw_gate("cko1", "cko1_podf", base + 0x60, 7); + hws[IMX6QDL_CLK_CKO2] = imx_clk_hw_gate("cko2", "cko2_podf", base + 0x60, 24); + + /* + * The gpt_3m clock is not available on i.MX6Q TO1.0. Let's point it + * to clock gpt_ipg_per to ease the gpt driver code. + */ + if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_1_0) + hws[IMX6QDL_CLK_GPT_3M] = hws[IMX6QDL_CLK_GPT_IPG_PER]; + + imx_check_clk_hws(hws, IMX6QDL_CLK_END); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + + clk_hw_register_clkdev(hws[IMX6QDL_CLK_ENET_REF], "enet_ref", NULL); + + clk_set_rate(hws[IMX6QDL_CLK_PLL3_PFD1_540M]->clk, 540000000); + if (clk_on_imx6dl()) + clk_set_parent(hws[IMX6QDL_CLK_IPU1_SEL]->clk, hws[IMX6QDL_CLK_PLL3_PFD1_540M]->clk); + + clk_set_parent(hws[IMX6QDL_CLK_IPU1_DI0_PRE_SEL]->clk, hws[IMX6QDL_CLK_PLL5_VIDEO_DIV]->clk); + clk_set_parent(hws[IMX6QDL_CLK_IPU1_DI1_PRE_SEL]->clk, hws[IMX6QDL_CLK_PLL5_VIDEO_DIV]->clk); + clk_set_parent(hws[IMX6QDL_CLK_IPU2_DI0_PRE_SEL]->clk, hws[IMX6QDL_CLK_PLL5_VIDEO_DIV]->clk); + clk_set_parent(hws[IMX6QDL_CLK_IPU2_DI1_PRE_SEL]->clk, hws[IMX6QDL_CLK_PLL5_VIDEO_DIV]->clk); + clk_set_parent(hws[IMX6QDL_CLK_IPU1_DI0_SEL]->clk, hws[IMX6QDL_CLK_IPU1_DI0_PRE]->clk); + clk_set_parent(hws[IMX6QDL_CLK_IPU1_DI1_SEL]->clk, hws[IMX6QDL_CLK_IPU1_DI1_PRE]->clk); + clk_set_parent(hws[IMX6QDL_CLK_IPU2_DI0_SEL]->clk, hws[IMX6QDL_CLK_IPU2_DI0_PRE]->clk); + clk_set_parent(hws[IMX6QDL_CLK_IPU2_DI1_SEL]->clk, hws[IMX6QDL_CLK_IPU2_DI1_PRE]->clk); + + /* + * The gpmi needs 100MHz frequency in the EDO/Sync mode, + * We can not get the 100MHz from the pll2_pfd0_352m. + * So choose pll2_pfd2_396m as enfc_sel's parent. + */ + clk_set_parent(hws[IMX6QDL_CLK_ENFC_SEL]->clk, hws[IMX6QDL_CLK_PLL2_PFD2_396M]->clk); + + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(hws[IMX6QDL_CLK_USBPHY1_GATE]->clk); + clk_prepare_enable(hws[IMX6QDL_CLK_USBPHY2_GATE]->clk); + } + + /* + * Let's initially set up CLKO with OSC24M, since this configuration + * is widely used by imx6q board designs to clock audio codec. + */ + ret = clk_set_parent(hws[IMX6QDL_CLK_CKO2_SEL]->clk, hws[IMX6QDL_CLK_OSC]->clk); + if (!ret) + ret = clk_set_parent(hws[IMX6QDL_CLK_CKO]->clk, hws[IMX6QDL_CLK_CKO2]->clk); + if (ret) + pr_warn("failed to set up CLKO: %d\n", ret); + + /* Audio-related clocks configuration */ + clk_set_parent(hws[IMX6QDL_CLK_SPDIF_SEL]->clk, hws[IMX6QDL_CLK_PLL3_PFD3_454M]->clk); + + /* All existing boards with PCIe use LVDS1 */ + if (IS_ENABLED(CONFIG_PCI_IMX6)) + clk_set_parent(hws[IMX6QDL_CLK_LVDS1_SEL]->clk, hws[IMX6QDL_CLK_SATA_REF_100M]->clk); + + /* + * Initialize the GPU clock muxes, so that the maximum specified clock + * rates for the respective SoC are not exceeded. + */ + if (clk_on_imx6dl()) { + clk_set_parent(hws[IMX6QDL_CLK_GPU3D_CORE_SEL]->clk, + hws[IMX6QDL_CLK_PLL2_PFD1_594M]->clk); + clk_set_parent(hws[IMX6QDL_CLK_GPU2D_CORE_SEL]->clk, + hws[IMX6QDL_CLK_PLL2_PFD1_594M]->clk); + } else if (clk_on_imx6q()) { + clk_set_parent(hws[IMX6QDL_CLK_GPU3D_CORE_SEL]->clk, + hws[IMX6QDL_CLK_MMDC_CH0_AXI]->clk); + clk_set_parent(hws[IMX6QDL_CLK_GPU3D_SHADER_SEL]->clk, + hws[IMX6QDL_CLK_PLL2_PFD1_594M]->clk); + clk_set_parent(hws[IMX6QDL_CLK_GPU2D_CORE_SEL]->clk, + hws[IMX6QDL_CLK_PLL3_USB_OTG]->clk); + } + + imx_register_uart_clocks(2); +} +CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init); diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c new file mode 100644 index 000000000..277365970 --- /dev/null +++ b/drivers/clk/imx/clk-imx6sl.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2013-2014 Freescale Semiconductor, Inc. + */ + +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk/imx.h> +#include <linux/err.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <dt-bindings/clock/imx6sl-clock.h> + +#include "clk.h" + +#define CCSR 0xc +#define BM_CCSR_PLL1_SW_CLK_SEL BIT(2) +#define CACRR 0x10 +#define CDHIPR 0x48 +#define BM_CDHIPR_ARM_PODF_BUSY BIT(16) +#define ARM_WAIT_DIV_396M 2 +#define ARM_WAIT_DIV_792M 4 +#define ARM_WAIT_DIV_996M 6 + +#define PLL_ARM 0x0 +#define BM_PLL_ARM_DIV_SELECT 0x7f +#define BM_PLL_ARM_POWERDOWN BIT(12) +#define BM_PLL_ARM_ENABLE BIT(13) +#define BM_PLL_ARM_LOCK BIT(31) +#define PLL_ARM_DIV_792M 66 + +static const char *step_sels[] = { "osc", "pll2_pfd2", }; +static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char *ocram_alt_sels[] = { "pll2_pfd2", "pll3_pfd1", }; +static const char *ocram_sels[] = { "periph", "ocram_alt_sels", }; +static const char *pre_periph_sels[] = { "pll2_bus", "pll2_pfd2", "pll2_pfd0", "pll2_198m", }; +static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", "dummy", }; +static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "pll2_bus", }; +static const char *periph_sels[] = { "pre_periph_sel", "periph_clk2_podf", }; +static const char *periph2_sels[] = { "pre_periph2_sel", "periph2_clk2_podf", }; +static const char *csi_sels[] = { "osc", "pll2_pfd2", "pll3_120m", "pll3_pfd1", }; +static const char *lcdif_axi_sels[] = { "pll2_bus", "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", }; +static const char *usdhc_sels[] = { "pll2_pfd2", "pll2_pfd0", }; +static const char *ssi_sels[] = { "pll3_pfd2", "pll3_pfd3", "pll4_audio_div", "dummy", }; +static const char *perclk_sels[] = { "ipg", "osc", }; +static const char *pxp_axi_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd3", }; +static const char *epdc_axi_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd2", "pll3_pfd2", }; +static const char *gpu2d_ovg_sels[] = { "pll3_pfd1", "pll3_usb_otg", "pll2_bus", "pll2_pfd2", }; +static const char *gpu2d_sels[] = { "pll2_pfd2", "pll3_usb_otg", "pll3_pfd1", "pll2_bus", }; +static const char *lcdif_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll3_pfd0", "pll3_pfd1", }; +static const char *epdc_pix_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0", "pll2_pfd1", "pll3_pfd1", }; +static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2", "pll3_pfd3", "pll3_usb_otg", }; +static const char *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char *uart_sels[] = { "pll3_80m", "osc", }; +static const char *lvds_sels[] = { + "pll1_sys", "pll2_bus", "pll2_pfd0", "pll2_pfd1", "pll2_pfd2", "dummy", "pll4_audio", "pll5_video", + "dummy", "enet_ref", "dummy", "dummy", "pll3_usb_otg", "pll7_usb_host", "pll3_pfd0", "pll3_pfd1", + "pll3_pfd2", "pll3_pfd3", "osc", "dummy", "dummy", "dummy", "dummy", "dummy", + "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", +}; +static const char *pll_bypass_src_sels[] = { "osc", "lvds1_in", }; +static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", }; +static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", }; +static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", }; +static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", }; +static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; +static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; +static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; + +static const struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, + { } +}; + +static const struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static const struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { } +}; + +static unsigned int share_count_ssi1; +static unsigned int share_count_ssi2; +static unsigned int share_count_ssi3; +static unsigned int share_count_spdif; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; +static void __iomem *ccm_base; +static void __iomem *anatop_base; + +/* + * ERR005311 CCM: After exit from WAIT mode, unwanted interrupt(s) taken + * during WAIT mode entry process could cause cache memory + * corruption. + * + * Software workaround: + * To prevent this issue from occurring, software should ensure that the + * ARM to IPG clock ratio is less than 12:5 (that is < 2.4x), before + * entering WAIT mode. + * + * This function will set the ARM clk to max value within the 12:5 limit. + * As IPG clock is fixed at 66MHz(so ARM freq must not exceed 158.4MHz), + * ARM freq are one of below setpoints: 396MHz, 792MHz and 996MHz, since + * the clk APIs can NOT be called in idle thread(may cause kernel schedule + * as there is sleep function in PLL wait function), so here we just slow + * down ARM to below freq according to previous freq: + * + * run mode wait mode + * 396MHz -> 132MHz; + * 792MHz -> 158.4MHz; + * 996MHz -> 142.3MHz; + */ +static int imx6sl_get_arm_divider_for_wait(void) +{ + if (readl_relaxed(ccm_base + CCSR) & BM_CCSR_PLL1_SW_CLK_SEL) { + return ARM_WAIT_DIV_396M; + } else { + if ((readl_relaxed(anatop_base + PLL_ARM) & + BM_PLL_ARM_DIV_SELECT) == PLL_ARM_DIV_792M) + return ARM_WAIT_DIV_792M; + else + return ARM_WAIT_DIV_996M; + } +} + +static void imx6sl_enable_pll_arm(bool enable) +{ + static u32 saved_pll_arm; + u32 val; + + if (enable) { + saved_pll_arm = val = readl_relaxed(anatop_base + PLL_ARM); + val |= BM_PLL_ARM_ENABLE; + val &= ~BM_PLL_ARM_POWERDOWN; + writel_relaxed(val, anatop_base + PLL_ARM); + while (!(readl_relaxed(anatop_base + PLL_ARM) & BM_PLL_ARM_LOCK)) + ; + } else { + writel_relaxed(saved_pll_arm, anatop_base + PLL_ARM); + } +} + +void imx6sl_set_wait_clk(bool enter) +{ + static unsigned long saved_arm_div; + int arm_div_for_wait = imx6sl_get_arm_divider_for_wait(); + + /* + * According to hardware design, arm podf change need + * PLL1 clock enabled. + */ + if (arm_div_for_wait == ARM_WAIT_DIV_396M) + imx6sl_enable_pll_arm(true); + + if (enter) { + saved_arm_div = readl_relaxed(ccm_base + CACRR); + writel_relaxed(arm_div_for_wait, ccm_base + CACRR); + } else { + writel_relaxed(saved_arm_div, ccm_base + CACRR); + } + while (__raw_readl(ccm_base + CDHIPR) & BM_CDHIPR_ARM_PODF_BUSY) + ; + + if (arm_div_for_wait == ARM_WAIT_DIV_396M) + imx6sl_enable_pll_arm(false); +} + +static void __init imx6sl_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + int ret; + + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + IMX6SL_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return; + + clk_hw_data->num = IMX6SL_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX6SL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + hws[IMX6SL_CLK_CKIL] = imx_obtain_fixed_clock_hw("ckil", 0); + hws[IMX6SL_CLK_OSC] = imx_obtain_fixed_clock_hw("osc", 0); + /* Clock source from external clock via CLK1 PAD */ + hws[IMX6SL_CLK_ANACLK1] = imx_obtain_fixed_clock_hw("anaclk1", 0); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sl-anatop"); + base = of_iomap(np, 0); + WARN_ON(!base); + of_node_put(np); + anatop_base = base; + + hws[IMX6SL_PLL1_BYPASS_SRC] = imx_clk_hw_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SL_PLL2_BYPASS_SRC] = imx_clk_hw_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SL_PLL3_BYPASS_SRC] = imx_clk_hw_mux("pll3_bypass_src", base + 0x10, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SL_PLL4_BYPASS_SRC] = imx_clk_hw_mux("pll4_bypass_src", base + 0x70, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SL_PLL5_BYPASS_SRC] = imx_clk_hw_mux("pll5_bypass_src", base + 0xa0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SL_PLL6_BYPASS_SRC] = imx_clk_hw_mux("pll6_bypass_src", base + 0xe0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SL_PLL7_BYPASS_SRC] = imx_clk_hw_mux("pll7_bypass_src", base + 0x20, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + + /* type name parent_name base div_mask */ + hws[IMX6SL_CLK_PLL1] = imx_clk_hw_pllv3(IMX_PLLV3_SYS, "pll1", "osc", base + 0x00, 0x7f); + hws[IMX6SL_CLK_PLL2] = imx_clk_hw_pllv3(IMX_PLLV3_GENERIC, "pll2", "osc", base + 0x30, 0x1); + hws[IMX6SL_CLK_PLL3] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll3", "osc", base + 0x10, 0x3); + hws[IMX6SL_CLK_PLL4] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll4", "osc", base + 0x70, 0x7f); + hws[IMX6SL_CLK_PLL5] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll5", "osc", base + 0xa0, 0x7f); + hws[IMX6SL_CLK_PLL6] = imx_clk_hw_pllv3(IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3); + hws[IMX6SL_CLK_PLL7] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll7", "osc", base + 0x20, 0x3); + + hws[IMX6SL_PLL1_BYPASS] = imx_clk_hw_mux_flags("pll1_bypass", base + 0x00, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SL_PLL2_BYPASS] = imx_clk_hw_mux_flags("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SL_PLL3_BYPASS] = imx_clk_hw_mux_flags("pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SL_PLL4_BYPASS] = imx_clk_hw_mux_flags("pll4_bypass", base + 0x70, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SL_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SL_PLL6_BYPASS] = imx_clk_hw_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SL_PLL7_BYPASS] = imx_clk_hw_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); + + /* Do not bypass PLLs initially */ + clk_set_parent(hws[IMX6SL_PLL1_BYPASS]->clk, hws[IMX6SL_CLK_PLL1]->clk); + clk_set_parent(hws[IMX6SL_PLL2_BYPASS]->clk, hws[IMX6SL_CLK_PLL2]->clk); + clk_set_parent(hws[IMX6SL_PLL3_BYPASS]->clk, hws[IMX6SL_CLK_PLL3]->clk); + clk_set_parent(hws[IMX6SL_PLL4_BYPASS]->clk, hws[IMX6SL_CLK_PLL4]->clk); + clk_set_parent(hws[IMX6SL_PLL5_BYPASS]->clk, hws[IMX6SL_CLK_PLL5]->clk); + clk_set_parent(hws[IMX6SL_PLL6_BYPASS]->clk, hws[IMX6SL_CLK_PLL6]->clk); + clk_set_parent(hws[IMX6SL_PLL7_BYPASS]->clk, hws[IMX6SL_CLK_PLL7]->clk); + + hws[IMX6SL_CLK_PLL1_SYS] = imx_clk_hw_gate("pll1_sys", "pll1_bypass", base + 0x00, 13); + hws[IMX6SL_CLK_PLL2_BUS] = imx_clk_hw_gate("pll2_bus", "pll2_bypass", base + 0x30, 13); + hws[IMX6SL_CLK_PLL3_USB_OTG] = imx_clk_hw_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13); + hws[IMX6SL_CLK_PLL4_AUDIO] = imx_clk_hw_gate("pll4_audio", "pll4_bypass", base + 0x70, 13); + hws[IMX6SL_CLK_PLL5_VIDEO] = imx_clk_hw_gate("pll5_video", "pll5_bypass", base + 0xa0, 13); + hws[IMX6SL_CLK_PLL6_ENET] = imx_clk_hw_gate("pll6_enet", "pll6_bypass", base + 0xe0, 13); + hws[IMX6SL_CLK_PLL7_USB_HOST] = imx_clk_hw_gate("pll7_usb_host", "pll7_bypass", base + 0x20, 13); + + hws[IMX6SL_CLK_LVDS1_SEL] = imx_clk_hw_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + hws[IMX6SL_CLK_LVDS1_OUT] = imx_clk_hw_gate_exclusive("lvds1_out", "lvds1_sel", base + 0x160, 10, BIT(12)); + hws[IMX6SL_CLK_LVDS1_IN] = imx_clk_hw_gate_exclusive("lvds1_in", "anaclk1", base + 0x160, 12, BIT(10)); + + /* + * usbphy1 and usbphy2 are implemented as dummy gates using reserve + * bit 20. They are used by phy driver to keep the refcount of + * parent PLL correct. usbphy1_gate and usbphy2_gate only needs to be + * turned on during boot, and software will not need to control it + * anymore after that. + */ + hws[IMX6SL_CLK_USBPHY1] = imx_clk_hw_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + hws[IMX6SL_CLK_USBPHY2] = imx_clk_hw_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + hws[IMX6SL_CLK_USBPHY1_GATE] = imx_clk_hw_gate("usbphy1_gate", "dummy", base + 0x10, 6); + hws[IMX6SL_CLK_USBPHY2_GATE] = imx_clk_hw_gate("usbphy2_gate", "dummy", base + 0x20, 6); + + /* dev name parent_name flags reg shift width div: flags, div_table lock */ + hws[IMX6SL_CLK_PLL4_POST_DIV] = clk_hw_register_divider_table(NULL, "pll4_post_div", "pll4_audio", CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6SL_CLK_PLL4_AUDIO_DIV] = clk_hw_register_divider(NULL, "pll4_audio_div", "pll4_post_div", CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); + hws[IMX6SL_CLK_PLL5_POST_DIV] = clk_hw_register_divider_table(NULL, "pll5_post_div", "pll5_video", CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6SL_CLK_PLL5_VIDEO_DIV] = clk_hw_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + hws[IMX6SL_CLK_ENET_REF] = clk_hw_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, base + 0xe0, 0, 2, 0, clk_enet_ref_table, &imx_ccm_lock); + + /* name parent_name reg idx */ + hws[IMX6SL_CLK_PLL2_PFD0] = imx_clk_hw_pfd("pll2_pfd0", "pll2_bus", base + 0x100, 0); + hws[IMX6SL_CLK_PLL2_PFD1] = imx_clk_hw_pfd("pll2_pfd1", "pll2_bus", base + 0x100, 1); + hws[IMX6SL_CLK_PLL2_PFD2] = imx_clk_hw_pfd("pll2_pfd2", "pll2_bus", base + 0x100, 2); + hws[IMX6SL_CLK_PLL3_PFD0] = imx_clk_hw_pfd("pll3_pfd0", "pll3_usb_otg", base + 0xf0, 0); + hws[IMX6SL_CLK_PLL3_PFD1] = imx_clk_hw_pfd("pll3_pfd1", "pll3_usb_otg", base + 0xf0, 1); + hws[IMX6SL_CLK_PLL3_PFD2] = imx_clk_hw_pfd("pll3_pfd2", "pll3_usb_otg", base + 0xf0, 2); + hws[IMX6SL_CLK_PLL3_PFD3] = imx_clk_hw_pfd("pll3_pfd3", "pll3_usb_otg", base + 0xf0, 3); + + /* name parent_name mult div */ + hws[IMX6SL_CLK_PLL2_198M] = imx_clk_hw_fixed_factor("pll2_198m", "pll2_pfd2", 1, 2); + hws[IMX6SL_CLK_PLL3_120M] = imx_clk_hw_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + hws[IMX6SL_CLK_PLL3_80M] = imx_clk_hw_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + hws[IMX6SL_CLK_PLL3_60M] = imx_clk_hw_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + ccm_base = base; + + /* name reg shift width parent_names num_parents */ + hws[IMX6SL_CLK_STEP] = imx_clk_hw_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + hws[IMX6SL_CLK_PLL1_SW] = imx_clk_hw_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); + hws[IMX6SL_CLK_OCRAM_ALT_SEL] = imx_clk_hw_mux("ocram_alt_sel", base + 0x14, 7, 1, ocram_alt_sels, ARRAY_SIZE(ocram_alt_sels)); + hws[IMX6SL_CLK_OCRAM_SEL] = imx_clk_hw_mux("ocram_sel", base + 0x14, 6, 1, ocram_sels, ARRAY_SIZE(ocram_sels)); + hws[IMX6SL_CLK_PRE_PERIPH2_SEL] = imx_clk_hw_mux("pre_periph2_sel", base + 0x18, 21, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels)); + hws[IMX6SL_CLK_PRE_PERIPH_SEL] = imx_clk_hw_mux("pre_periph_sel", base + 0x18, 18, 2, pre_periph_sels, ARRAY_SIZE(pre_periph_sels)); + hws[IMX6SL_CLK_PERIPH2_CLK2_SEL] = imx_clk_hw_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + hws[IMX6SL_CLK_PERIPH_CLK2_SEL] = imx_clk_hw_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + hws[IMX6SL_CLK_CSI_SEL] = imx_clk_hw_mux("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels)); + hws[IMX6SL_CLK_LCDIF_AXI_SEL] = imx_clk_hw_mux("lcdif_axi_sel", base + 0x3c, 14, 2, lcdif_axi_sels, ARRAY_SIZE(lcdif_axi_sels)); + hws[IMX6SL_CLK_USDHC1_SEL] = imx_clk_hw_fixup_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_USDHC2_SEL] = imx_clk_hw_fixup_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_USDHC3_SEL] = imx_clk_hw_fixup_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_USDHC4_SEL] = imx_clk_hw_fixup_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_SSI1_SEL] = imx_clk_hw_fixup_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_SSI2_SEL] = imx_clk_hw_fixup_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_SSI3_SEL] = imx_clk_hw_fixup_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_PERCLK_SEL] = imx_clk_hw_fixup_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels), imx_cscmr1_fixup); + hws[IMX6SL_CLK_PXP_AXI_SEL] = imx_clk_hw_mux("pxp_axi_sel", base + 0x34, 6, 3, pxp_axi_sels, ARRAY_SIZE(pxp_axi_sels)); + hws[IMX6SL_CLK_EPDC_AXI_SEL] = imx_clk_hw_mux("epdc_axi_sel", base + 0x34, 15, 3, epdc_axi_sels, ARRAY_SIZE(epdc_axi_sels)); + hws[IMX6SL_CLK_GPU2D_OVG_SEL] = imx_clk_hw_mux("gpu2d_ovg_sel", base + 0x18, 4, 2, gpu2d_ovg_sels, ARRAY_SIZE(gpu2d_ovg_sels)); + hws[IMX6SL_CLK_GPU2D_SEL] = imx_clk_hw_mux("gpu2d_sel", base + 0x18, 8, 2, gpu2d_sels, ARRAY_SIZE(gpu2d_sels)); + hws[IMX6SL_CLK_LCDIF_PIX_SEL] = imx_clk_hw_mux("lcdif_pix_sel", base + 0x38, 6, 3, lcdif_pix_sels, ARRAY_SIZE(lcdif_pix_sels)); + hws[IMX6SL_CLK_EPDC_PIX_SEL] = imx_clk_hw_mux("epdc_pix_sel", base + 0x38, 15, 3, epdc_pix_sels, ARRAY_SIZE(epdc_pix_sels)); + hws[IMX6SL_CLK_SPDIF0_SEL] = imx_clk_hw_mux("spdif0_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6SL_CLK_SPDIF1_SEL] = imx_clk_hw_mux("spdif1_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6SL_CLK_EXTERN_AUDIO_SEL] = imx_clk_hw_mux("extern_audio_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6SL_CLK_ECSPI_SEL] = imx_clk_hw_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + hws[IMX6SL_CLK_UART_SEL] = imx_clk_hw_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + + /* name reg shift width busy: reg, shift parent_names num_parents */ + hws[IMX6SL_CLK_PERIPH] = imx_clk_hw_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + hws[IMX6SL_CLK_PERIPH2] = imx_clk_hw_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + + /* name parent_name reg shift width */ + hws[IMX6SL_CLK_OCRAM_PODF] = imx_clk_hw_busy_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3, base + 0x48, 0); + hws[IMX6SL_CLK_PERIPH_CLK2_PODF] = imx_clk_hw_divider("periph_clk2_podf", "periph_clk2_sel", base + 0x14, 27, 3); + hws[IMX6SL_CLK_PERIPH2_CLK2_PODF] = imx_clk_hw_divider("periph2_clk2_podf", "periph2_clk2_sel", base + 0x14, 0, 3); + hws[IMX6SL_CLK_IPG] = imx_clk_hw_divider("ipg", "ahb", base + 0x14, 8, 2); + hws[IMX6SL_CLK_CSI_PODF] = imx_clk_hw_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); + hws[IMX6SL_CLK_LCDIF_AXI_PODF] = imx_clk_hw_divider("lcdif_axi_podf", "lcdif_axi_sel", base + 0x3c, 16, 3); + hws[IMX6SL_CLK_USDHC1_PODF] = imx_clk_hw_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + hws[IMX6SL_CLK_USDHC2_PODF] = imx_clk_hw_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + hws[IMX6SL_CLK_USDHC3_PODF] = imx_clk_hw_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + hws[IMX6SL_CLK_USDHC4_PODF] = imx_clk_hw_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); + hws[IMX6SL_CLK_SSI1_PRED] = imx_clk_hw_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + hws[IMX6SL_CLK_SSI1_PODF] = imx_clk_hw_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + hws[IMX6SL_CLK_SSI2_PRED] = imx_clk_hw_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + hws[IMX6SL_CLK_SSI2_PODF] = imx_clk_hw_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + hws[IMX6SL_CLK_SSI3_PRED] = imx_clk_hw_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + hws[IMX6SL_CLK_SSI3_PODF] = imx_clk_hw_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + hws[IMX6SL_CLK_PERCLK] = imx_clk_hw_fixup_divider("perclk", "perclk_sel", base + 0x1c, 0, 6, imx_cscmr1_fixup); + hws[IMX6SL_CLK_PXP_AXI_PODF] = imx_clk_hw_divider("pxp_axi_podf", "pxp_axi_sel", base + 0x34, 3, 3); + hws[IMX6SL_CLK_EPDC_AXI_PODF] = imx_clk_hw_divider("epdc_axi_podf", "epdc_axi_sel", base + 0x34, 12, 3); + hws[IMX6SL_CLK_GPU2D_OVG_PODF] = imx_clk_hw_divider("gpu2d_ovg_podf", "gpu2d_ovg_sel", base + 0x18, 26, 3); + hws[IMX6SL_CLK_GPU2D_PODF] = imx_clk_hw_divider("gpu2d_podf", "gpu2d_sel", base + 0x18, 29, 3); + hws[IMX6SL_CLK_LCDIF_PIX_PRED] = imx_clk_hw_divider("lcdif_pix_pred", "lcdif_pix_sel", base + 0x38, 3, 3); + hws[IMX6SL_CLK_EPDC_PIX_PRED] = imx_clk_hw_divider("epdc_pix_pred", "epdc_pix_sel", base + 0x38, 12, 3); + hws[IMX6SL_CLK_LCDIF_PIX_PODF] = imx_clk_hw_fixup_divider("lcdif_pix_podf", "lcdif_pix_pred", base + 0x1c, 20, 3, imx_cscmr1_fixup); + hws[IMX6SL_CLK_EPDC_PIX_PODF] = imx_clk_hw_divider("epdc_pix_podf", "epdc_pix_pred", base + 0x18, 23, 3); + hws[IMX6SL_CLK_SPDIF0_PRED] = imx_clk_hw_divider("spdif0_pred", "spdif0_sel", base + 0x30, 25, 3); + hws[IMX6SL_CLK_SPDIF0_PODF] = imx_clk_hw_divider("spdif0_podf", "spdif0_pred", base + 0x30, 22, 3); + hws[IMX6SL_CLK_SPDIF1_PRED] = imx_clk_hw_divider("spdif1_pred", "spdif1_sel", base + 0x30, 12, 3); + hws[IMX6SL_CLK_SPDIF1_PODF] = imx_clk_hw_divider("spdif1_podf", "spdif1_pred", base + 0x30, 9, 3); + hws[IMX6SL_CLK_EXTERN_AUDIO_PRED] = imx_clk_hw_divider("extern_audio_pred", "extern_audio_sel", base + 0x28, 9, 3); + hws[IMX6SL_CLK_EXTERN_AUDIO_PODF] = imx_clk_hw_divider("extern_audio_podf", "extern_audio_pred", base + 0x28, 25, 3); + hws[IMX6SL_CLK_ECSPI_ROOT] = imx_clk_hw_divider("ecspi_root", "ecspi_sel", base + 0x38, 19, 6); + hws[IMX6SL_CLK_UART_ROOT] = imx_clk_hw_divider("uart_root", "uart_sel", base + 0x24, 0, 6); + + /* name parent_name reg shift width busy: reg, shift */ + hws[IMX6SL_CLK_AHB] = imx_clk_hw_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + hws[IMX6SL_CLK_MMDC_ROOT] = imx_clk_hw_busy_divider("mmdc", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + hws[IMX6SL_CLK_ARM] = imx_clk_hw_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + + /* name parent_name reg shift */ + hws[IMX6SL_CLK_ECSPI1] = imx_clk_hw_gate2("ecspi1", "ecspi_root", base + 0x6c, 0); + hws[IMX6SL_CLK_ECSPI2] = imx_clk_hw_gate2("ecspi2", "ecspi_root", base + 0x6c, 2); + hws[IMX6SL_CLK_ECSPI3] = imx_clk_hw_gate2("ecspi3", "ecspi_root", base + 0x6c, 4); + hws[IMX6SL_CLK_ECSPI4] = imx_clk_hw_gate2("ecspi4", "ecspi_root", base + 0x6c, 6); + hws[IMX6SL_CLK_ENET] = imx_clk_hw_gate2("enet", "ipg", base + 0x6c, 10); + hws[IMX6SL_CLK_EPIT1] = imx_clk_hw_gate2("epit1", "perclk", base + 0x6c, 12); + hws[IMX6SL_CLK_EPIT2] = imx_clk_hw_gate2("epit2", "perclk", base + 0x6c, 14); + hws[IMX6SL_CLK_EXTERN_AUDIO] = imx_clk_hw_gate2("extern_audio", "extern_audio_podf", base + 0x6c, 16); + hws[IMX6SL_CLK_GPT] = imx_clk_hw_gate2("gpt", "perclk", base + 0x6c, 20); + hws[IMX6SL_CLK_GPT_SERIAL] = imx_clk_hw_gate2("gpt_serial", "perclk", base + 0x6c, 22); + hws[IMX6SL_CLK_GPU2D_OVG] = imx_clk_hw_gate2("gpu2d_ovg", "gpu2d_ovg_podf", base + 0x6c, 26); + hws[IMX6SL_CLK_I2C1] = imx_clk_hw_gate2("i2c1", "perclk", base + 0x70, 6); + hws[IMX6SL_CLK_I2C2] = imx_clk_hw_gate2("i2c2", "perclk", base + 0x70, 8); + hws[IMX6SL_CLK_I2C3] = imx_clk_hw_gate2("i2c3", "perclk", base + 0x70, 10); + hws[IMX6SL_CLK_OCOTP] = imx_clk_hw_gate2("ocotp", "ipg", base + 0x70, 12); + hws[IMX6SL_CLK_CSI] = imx_clk_hw_gate2("csi", "csi_podf", base + 0x74, 0); + hws[IMX6SL_CLK_PXP_AXI] = imx_clk_hw_gate2("pxp_axi", "pxp_axi_podf", base + 0x74, 2); + hws[IMX6SL_CLK_EPDC_AXI] = imx_clk_hw_gate2("epdc_axi", "epdc_axi_podf", base + 0x74, 4); + hws[IMX6SL_CLK_LCDIF_AXI] = imx_clk_hw_gate2("lcdif_axi", "lcdif_axi_podf", base + 0x74, 6); + hws[IMX6SL_CLK_LCDIF_PIX] = imx_clk_hw_gate2("lcdif_pix", "lcdif_pix_podf", base + 0x74, 8); + hws[IMX6SL_CLK_EPDC_PIX] = imx_clk_hw_gate2("epdc_pix", "epdc_pix_podf", base + 0x74, 10); + hws[IMX6SL_CLK_MMDC_P0_IPG] = imx_clk_hw_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + hws[IMX6SL_CLK_MMDC_P1_IPG] = imx_clk_hw_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26); + hws[IMX6SL_CLK_OCRAM] = imx_clk_hw_gate2("ocram", "ocram_podf", base + 0x74, 28); + hws[IMX6SL_CLK_PWM1] = imx_clk_hw_gate2("pwm1", "perclk", base + 0x78, 16); + hws[IMX6SL_CLK_PWM2] = imx_clk_hw_gate2("pwm2", "perclk", base + 0x78, 18); + hws[IMX6SL_CLK_PWM3] = imx_clk_hw_gate2("pwm3", "perclk", base + 0x78, 20); + hws[IMX6SL_CLK_PWM4] = imx_clk_hw_gate2("pwm4", "perclk", base + 0x78, 22); + hws[IMX6SL_CLK_SDMA] = imx_clk_hw_gate2("sdma", "ipg", base + 0x7c, 6); + hws[IMX6SL_CLK_SPBA] = imx_clk_hw_gate2("spba", "ipg", base + 0x7c, 12); + hws[IMX6SL_CLK_SPDIF] = imx_clk_hw_gate2_shared("spdif", "spdif0_podf", base + 0x7c, 14, &share_count_spdif); + hws[IMX6SL_CLK_SPDIF_GCLK] = imx_clk_hw_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_spdif); + hws[IMX6SL_CLK_SSI1_IPG] = imx_clk_hw_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6SL_CLK_SSI2_IPG] = imx_clk_hw_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6SL_CLK_SSI3_IPG] = imx_clk_hw_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6SL_CLK_SSI1] = imx_clk_hw_gate2_shared("ssi1", "ssi1_podf", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6SL_CLK_SSI2] = imx_clk_hw_gate2_shared("ssi2", "ssi2_podf", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6SL_CLK_SSI3] = imx_clk_hw_gate2_shared("ssi3", "ssi3_podf", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6SL_CLK_UART] = imx_clk_hw_gate2("uart", "ipg", base + 0x7c, 24); + hws[IMX6SL_CLK_UART_SERIAL] = imx_clk_hw_gate2("uart_serial", "uart_root", base + 0x7c, 26); + hws[IMX6SL_CLK_USBOH3] = imx_clk_hw_gate2("usboh3", "ipg", base + 0x80, 0); + hws[IMX6SL_CLK_USDHC1] = imx_clk_hw_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + hws[IMX6SL_CLK_USDHC2] = imx_clk_hw_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + hws[IMX6SL_CLK_USDHC3] = imx_clk_hw_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + hws[IMX6SL_CLK_USDHC4] = imx_clk_hw_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + + /* Ensure the MMDC CH0 handshake is bypassed */ + imx_mmdc_mask_handshake(base, 0); + + imx_check_clk_hws(hws, IMX6SL_CLK_END); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + + /* Ensure the AHB clk is at 132MHz. */ + ret = clk_set_rate(hws[IMX6SL_CLK_AHB]->clk, 132000000); + if (ret) + pr_warn("%s: failed to set AHB clock rate %d!\n", + __func__, ret); + + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(hws[IMX6SL_CLK_USBPHY1_GATE]->clk); + clk_prepare_enable(hws[IMX6SL_CLK_USBPHY2_GATE]->clk); + } + + /* Audio-related clocks configuration */ + clk_set_parent(hws[IMX6SL_CLK_SPDIF0_SEL]->clk, hws[IMX6SL_CLK_PLL3_PFD3]->clk); + + /* set PLL5 video as lcdif pix parent clock */ + clk_set_parent(hws[IMX6SL_CLK_LCDIF_PIX_SEL]->clk, + hws[IMX6SL_CLK_PLL5_VIDEO_DIV]->clk); + + clk_set_parent(hws[IMX6SL_CLK_LCDIF_AXI_SEL]->clk, + hws[IMX6SL_CLK_PLL2_PFD2]->clk); + + imx_register_uart_clocks(2); +} +CLK_OF_DECLARE(imx6sl, "fsl,imx6sl-ccm", imx6sl_clocks_init); diff --git a/drivers/clk/imx/clk-imx6sll.c b/drivers/clk/imx/clk-imx6sll.c new file mode 100644 index 000000000..31d777f30 --- /dev/null +++ b/drivers/clk/imx/clk-imx6sll.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017-2018 NXP. + */ + +#include <dt-bindings/clock/imx6sll-clock.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk.h" + +#define CCM_ANALOG_PLL_BYPASS (0x1 << 16) +#define xPLL_CLR(offset) (offset + 0x8) + +static const char *pll_bypass_src_sels[] = { "osc", "dummy", }; +static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", }; +static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", }; +static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", }; +static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", }; +static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; +static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; +static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; +static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; +static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char *axi_alt_sels[] = { "pll2_pfd2_396m", "pll3_pfd1_540m", }; +static const char *axi_sels[] = {"periph", "axi_alt_sel", }; +static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +static const char *periph2_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll4_audio_div", }; +static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", }; +static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "osc", }; +static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; +static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; +static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *ssi_sels[] = {"pll3_pfd2_508m", "pll3_pfd3_454m", "pll4_audio_div", "dummy",}; +static const char *spdif_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", }; +static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; +static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; +static const char *ldb_di0_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_pfd3_594m", "pll2_pfd1_594m", "pll3_pfd3_454m", }; +static const char *ldb_di1_sels[] = { "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_bus", "pll3_pfd3_454m", "pll3_pfd2_508m", }; +static const char *lcdif_pre_sels[] = { "pll2_bus", "pll3_pfd3_454m", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd1_594m", "pll3_pfd1_540m", }; +static const char *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char *uart_sels[] = { "pll3_80m", "osc", }; +static const char *perclk_sels[] = { "ipg", "osc", }; +static const char *lcdif_sels[] = { "lcdif_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; + +static const char *epdc_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", }; +static const char *epdc_sels[] = { "epdc_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; + +static const struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static const struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { } +}; + +static u32 share_count_audio; +static u32 share_count_ssi1; +static u32 share_count_ssi2; +static u32 share_count_ssi3; + +static void __init imx6sll_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + IMX6SLL_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return; + + clk_hw_data->num = IMX6SLL_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX6SLL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + hws[IMX6SLL_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); + hws[IMX6SLL_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); + + /* ipp_di clock is external input */ + hws[IMX6SLL_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0"); + hws[IMX6SLL_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sll-anatop"); + base = of_iomap(np, 0); + of_node_put(np); + WARN_ON(!base); + + /* Do not bypass PLLs initially */ + writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x0)); + writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x10)); + writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x20)); + writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x30)); + writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0x70)); + writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0xa0)); + writel_relaxed(CCM_ANALOG_PLL_BYPASS, base + xPLL_CLR(0xe0)); + + hws[IMX6SLL_PLL1_BYPASS_SRC] = imx_clk_hw_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SLL_PLL2_BYPASS_SRC] = imx_clk_hw_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SLL_PLL3_BYPASS_SRC] = imx_clk_hw_mux("pll3_bypass_src", base + 0x10, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SLL_PLL4_BYPASS_SRC] = imx_clk_hw_mux("pll4_bypass_src", base + 0x70, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SLL_PLL5_BYPASS_SRC] = imx_clk_hw_mux("pll5_bypass_src", base + 0xa0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SLL_PLL6_BYPASS_SRC] = imx_clk_hw_mux("pll6_bypass_src", base + 0xe0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SLL_PLL7_BYPASS_SRC] = imx_clk_hw_mux("pll7_bypass_src", base + 0x20, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + + hws[IMX6SLL_CLK_PLL1] = imx_clk_hw_pllv3(IMX_PLLV3_SYS, "pll1", "pll1_bypass_src", base + 0x00, 0x7f); + hws[IMX6SLL_CLK_PLL2] = imx_clk_hw_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", base + 0x30, 0x1); + hws[IMX6SLL_CLK_PLL3] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll3", "pll3_bypass_src", base + 0x10, 0x3); + hws[IMX6SLL_CLK_PLL4] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", base + 0x70, 0x7f); + hws[IMX6SLL_CLK_PLL5] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll5", "pll5_bypass_src", base + 0xa0, 0x7f); + hws[IMX6SLL_CLK_PLL6] = imx_clk_hw_pllv3(IMX_PLLV3_ENET, "pll6", "pll6_bypass_src", base + 0xe0, 0x3); + hws[IMX6SLL_CLK_PLL7] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll7", "pll7_bypass_src", base + 0x20, 0x3); + + hws[IMX6SLL_PLL1_BYPASS] = imx_clk_hw_mux_flags("pll1_bypass", base + 0x00, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SLL_PLL2_BYPASS] = imx_clk_hw_mux_flags("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SLL_PLL3_BYPASS] = imx_clk_hw_mux_flags("pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SLL_PLL4_BYPASS] = imx_clk_hw_mux_flags("pll4_bypass", base + 0x70, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SLL_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SLL_PLL6_BYPASS] = imx_clk_hw_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SLL_PLL7_BYPASS] = imx_clk_hw_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); + + hws[IMX6SLL_CLK_PLL1_SYS] = imx_clk_hw_fixed_factor("pll1_sys", "pll1_bypass", 1, 1); + hws[IMX6SLL_CLK_PLL2_BUS] = imx_clk_hw_gate("pll2_bus", "pll2_bypass", base + 0x30, 13); + hws[IMX6SLL_CLK_PLL3_USB_OTG] = imx_clk_hw_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13); + hws[IMX6SLL_CLK_PLL4_AUDIO] = imx_clk_hw_gate("pll4_audio", "pll4_bypass", base + 0x70, 13); + hws[IMX6SLL_CLK_PLL5_VIDEO] = imx_clk_hw_gate("pll5_video", "pll5_bypass", base + 0xa0, 13); + hws[IMX6SLL_CLK_PLL6_ENET] = imx_clk_hw_gate("pll6_enet", "pll6_bypass", base + 0xe0, 13); + hws[IMX6SLL_CLK_PLL7_USB_HOST] = imx_clk_hw_gate("pll7_usb_host", "pll7_bypass", base + 0x20, 13); + + /* + * Bit 20 is the reserved and read-only bit, we do this only for: + * - Do nothing for usbphy clk_enable/disable + * - Keep refcount when do usbphy clk_enable/disable, in that case, + * the clk framework many need to enable/disable usbphy's parent + */ + hws[IMX6SLL_CLK_USBPHY1] = imx_clk_hw_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + hws[IMX6SLL_CLK_USBPHY2] = imx_clk_hw_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + + /* + * usbphy*_gate needs to be on after system boots up, and software + * never needs to control it anymore. + */ + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + hws[IMX6SLL_CLK_USBPHY1_GATE] = imx_clk_hw_gate_flags("usbphy1_gate", "dummy", base + 0x10, 6, CLK_IS_CRITICAL); + hws[IMX6SLL_CLK_USBPHY2_GATE] = imx_clk_hw_gate_flags("usbphy2_gate", "dummy", base + 0x20, 6, CLK_IS_CRITICAL); + } + + /* name parent_name reg idx */ + hws[IMX6SLL_CLK_PLL2_PFD0] = imx_clk_hw_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); + hws[IMX6SLL_CLK_PLL2_PFD1] = imx_clk_hw_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); + hws[IMX6SLL_CLK_PLL2_PFD2] = imx_clk_hw_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); + hws[IMX6SLL_CLK_PLL2_PFD3] = imx_clk_hw_pfd("pll2_pfd3_594m", "pll2_bus", base + 0x100, 3); + hws[IMX6SLL_CLK_PLL3_PFD0] = imx_clk_hw_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); + hws[IMX6SLL_CLK_PLL3_PFD1] = imx_clk_hw_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); + hws[IMX6SLL_CLK_PLL3_PFD2] = imx_clk_hw_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); + hws[IMX6SLL_CLK_PLL3_PFD3] = imx_clk_hw_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); + + hws[IMX6SLL_CLK_PLL4_POST_DIV] = clk_hw_register_divider_table(NULL, "pll4_post_div", "pll4_audio", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6SLL_CLK_PLL4_AUDIO_DIV] = clk_hw_register_divider(NULL, "pll4_audio_div", "pll4_post_div", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 15, 1, 0, &imx_ccm_lock); + hws[IMX6SLL_CLK_PLL5_POST_DIV] = clk_hw_register_divider_table(NULL, "pll5_post_div", "pll5_video", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6SLL_CLK_PLL5_VIDEO_DIV] = clk_hw_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + + /* name parent_name mult div */ + hws[IMX6SLL_CLK_PLL2_198M] = imx_clk_hw_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); + hws[IMX6SLL_CLK_PLL3_120M] = imx_clk_hw_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + hws[IMX6SLL_CLK_PLL3_80M] = imx_clk_hw_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + hws[IMX6SLL_CLK_PLL3_60M] = imx_clk_hw_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + + hws[IMX6SLL_CLK_STEP] = imx_clk_hw_mux("step", base + 0x0c, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + hws[IMX6SLL_CLK_PLL1_SW] = imx_clk_hw_mux_flags("pll1_sw", base + 0x0c, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels), 0); + hws[IMX6SLL_CLK_AXI_ALT_SEL] = imx_clk_hw_mux("axi_alt_sel", base + 0x14, 7, 1, axi_alt_sels, ARRAY_SIZE(axi_alt_sels)); + hws[IMX6SLL_CLK_AXI_SEL] = imx_clk_hw_mux_flags("axi_sel", base + 0x14, 6, 1, axi_sels, ARRAY_SIZE(axi_sels), 0); + hws[IMX6SLL_CLK_PERIPH_PRE] = imx_clk_hw_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + hws[IMX6SLL_CLK_PERIPH2_PRE] = imx_clk_hw_mux("periph2_pre", base + 0x18, 21, 2, periph2_pre_sels, ARRAY_SIZE(periph2_pre_sels)); + hws[IMX6SLL_CLK_PERIPH_CLK2_SEL] = imx_clk_hw_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + hws[IMX6SLL_CLK_PERIPH2_CLK2_SEL] = imx_clk_hw_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + hws[IMX6SLL_CLK_USDHC1_SEL] = imx_clk_hw_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6SLL_CLK_USDHC2_SEL] = imx_clk_hw_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6SLL_CLK_USDHC3_SEL] = imx_clk_hw_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6SLL_CLK_SSI1_SEL] = imx_clk_hw_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6SLL_CLK_SSI2_SEL] = imx_clk_hw_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6SLL_CLK_SSI3_SEL] = imx_clk_hw_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6SLL_CLK_PERCLK_SEL] = imx_clk_hw_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); + hws[IMX6SLL_CLK_UART_SEL] = imx_clk_hw_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + hws[IMX6SLL_CLK_SPDIF_SEL] = imx_clk_hw_mux("spdif_sel", base + 0x30, 20, 2, spdif_sels, ARRAY_SIZE(spdif_sels)); + hws[IMX6SLL_CLK_EXTERN_AUDIO_SEL] = imx_clk_hw_mux("extern_audio_sel", base + 0x30, 7, 2, spdif_sels, ARRAY_SIZE(spdif_sels)); + hws[IMX6SLL_CLK_EPDC_PRE_SEL] = imx_clk_hw_mux("epdc_pre_sel", base + 0x34, 15, 3, epdc_pre_sels, ARRAY_SIZE(epdc_pre_sels)); + hws[IMX6SLL_CLK_EPDC_SEL] = imx_clk_hw_mux("epdc_sel", base + 0x34, 9, 3, epdc_sels, ARRAY_SIZE(epdc_sels)); + hws[IMX6SLL_CLK_ECSPI_SEL] = imx_clk_hw_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + hws[IMX6SLL_CLK_LCDIF_PRE_SEL] = imx_clk_hw_mux("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels)); + hws[IMX6SLL_CLK_LCDIF_SEL] = imx_clk_hw_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels)); + + hws[IMX6SLL_CLK_PERIPH] = imx_clk_hw_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + hws[IMX6SLL_CLK_PERIPH2] = imx_clk_hw_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + + hws[IMX6SLL_CLK_PERIPH_CLK2] = imx_clk_hw_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); + hws[IMX6SLL_CLK_PERIPH2_CLK2] = imx_clk_hw_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + hws[IMX6SLL_CLK_IPG] = imx_clk_hw_divider("ipg", "ahb", base + 0x14, 8, 2); + hws[IMX6SLL_CLK_LCDIF_PODF] = imx_clk_hw_divider("lcdif_podf", "lcdif_pred", base + 0x18, 23, 3); + hws[IMX6SLL_CLK_PERCLK] = imx_clk_hw_divider("perclk", "perclk_sel", base + 0x1c, 0, 6); + hws[IMX6SLL_CLK_USDHC3_PODF] = imx_clk_hw_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + hws[IMX6SLL_CLK_USDHC2_PODF] = imx_clk_hw_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + hws[IMX6SLL_CLK_USDHC1_PODF] = imx_clk_hw_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + hws[IMX6SLL_CLK_UART_PODF] = imx_clk_hw_divider("uart_podf", "uart_sel", base + 0x24, 0, 6); + hws[IMX6SLL_CLK_SSI3_PRED] = imx_clk_hw_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + hws[IMX6SLL_CLK_SSI3_PODF] = imx_clk_hw_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + hws[IMX6SLL_CLK_SSI1_PRED] = imx_clk_hw_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + hws[IMX6SLL_CLK_SSI1_PODF] = imx_clk_hw_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + hws[IMX6SLL_CLK_SSI2_PRED] = imx_clk_hw_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + hws[IMX6SLL_CLK_SSI2_PODF] = imx_clk_hw_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + hws[IMX6SLL_CLK_SPDIF_PRED] = imx_clk_hw_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); + hws[IMX6SLL_CLK_SPDIF_PODF] = imx_clk_hw_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); + hws[IMX6SLL_CLK_EXTERN_AUDIO_PRED] = imx_clk_hw_divider("extern_audio_pred", "extern_audio_sel", base + 0x30, 12, 3); + hws[IMX6SLL_CLK_EXTERN_AUDIO_PODF] = imx_clk_hw_divider("extern_audio_podf", "extern_audio_pred", base + 0x30, 9, 3); + hws[IMX6SLL_CLK_EPDC_PODF] = imx_clk_hw_divider("epdc_podf", "epdc_pre_sel", base + 0x34, 12, 3); + hws[IMX6SLL_CLK_ECSPI_PODF] = imx_clk_hw_divider("ecspi_podf", "ecspi_sel", base + 0x38, 19, 6); + hws[IMX6SLL_CLK_LCDIF_PRED] = imx_clk_hw_divider("lcdif_pred", "lcdif_pre_sel", base + 0x38, 12, 3); + + hws[IMX6SLL_CLK_ARM] = imx_clk_hw_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + hws[IMX6SLL_CLK_MMDC_PODF] = imx_clk_hw_busy_divider("mmdc_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + hws[IMX6SLL_CLK_AXI_PODF] = imx_clk_hw_busy_divider("axi", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0); + hws[IMX6SLL_CLK_AHB] = imx_clk_hw_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + + hws[IMX6SLL_CLK_LDB_DI0_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); + hws[IMX6SLL_CLK_LDB_DI0_DIV_7] = imx_clk_hw_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); + hws[IMX6SLL_CLK_LDB_DI1_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); + hws[IMX6SLL_CLK_LDB_DI1_DIV_7] = imx_clk_hw_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7); + + hws[IMX6SLL_CLK_LDB_DI0_SEL] = imx_clk_hw_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels)); + hws[IMX6SLL_CLK_LDB_DI1_SEL] = imx_clk_hw_mux("ldb_di1_sel", base + 0x1c, 7, 3, ldb_di1_sels, ARRAY_SIZE(ldb_di1_sels)); + hws[IMX6SLL_CLK_LDB_DI0_DIV_SEL] = imx_clk_hw_mux("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels)); + hws[IMX6SLL_CLK_LDB_DI1_DIV_SEL] = imx_clk_hw_mux("ldb_di1_div_sel", base + 0x20, 10, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels)); + + /* CCGR0 */ + hws[IMX6SLL_CLK_AIPSTZ1] = imx_clk_hw_gate2_flags("aips_tz1", "ahb", base + 0x68, 0, CLK_IS_CRITICAL); + hws[IMX6SLL_CLK_AIPSTZ2] = imx_clk_hw_gate2_flags("aips_tz2", "ahb", base + 0x68, 2, CLK_IS_CRITICAL); + hws[IMX6SLL_CLK_DCP] = imx_clk_hw_gate2("dcp", "ahb", base + 0x68, 10); + hws[IMX6SLL_CLK_UART2_IPG] = imx_clk_hw_gate2("uart2_ipg", "ipg", base + 0x68, 28); + hws[IMX6SLL_CLK_UART2_SERIAL] = imx_clk_hw_gate2("uart2_serial", "uart_podf", base + 0x68, 28); + hws[IMX6SLL_CLK_GPIO2] = imx_clk_hw_gate2("gpio2", "ipg", base + 0x68, 30); + + /* CCGR1 */ + hws[IMX6SLL_CLK_ECSPI1] = imx_clk_hw_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); + hws[IMX6SLL_CLK_ECSPI2] = imx_clk_hw_gate2("ecspi2", "ecspi_podf", base + 0x6c, 2); + hws[IMX6SLL_CLK_ECSPI3] = imx_clk_hw_gate2("ecspi3", "ecspi_podf", base + 0x6c, 4); + hws[IMX6SLL_CLK_ECSPI4] = imx_clk_hw_gate2("ecspi4", "ecspi_podf", base + 0x6c, 6); + hws[IMX6SLL_CLK_UART3_IPG] = imx_clk_hw_gate2("uart3_ipg", "ipg", base + 0x6c, 10); + hws[IMX6SLL_CLK_UART3_SERIAL] = imx_clk_hw_gate2("uart3_serial", "uart_podf", base + 0x6c, 10); + hws[IMX6SLL_CLK_EPIT1] = imx_clk_hw_gate2("epit1", "perclk", base + 0x6c, 12); + hws[IMX6SLL_CLK_EPIT2] = imx_clk_hw_gate2("epit2", "perclk", base + 0x6c, 14); + hws[IMX6SLL_CLK_GPT_BUS] = imx_clk_hw_gate2("gpt1_bus", "perclk", base + 0x6c, 20); + hws[IMX6SLL_CLK_GPT_SERIAL] = imx_clk_hw_gate2("gpt1_serial", "perclk", base + 0x6c, 22); + hws[IMX6SLL_CLK_UART4_IPG] = imx_clk_hw_gate2("uart4_ipg", "ipg", base + 0x6c, 24); + hws[IMX6SLL_CLK_UART4_SERIAL] = imx_clk_hw_gate2("uart4_serial", "uart_podf", base + 0x6c, 24); + hws[IMX6SLL_CLK_GPIO1] = imx_clk_hw_gate2("gpio1", "ipg", base + 0x6c, 26); + hws[IMX6SLL_CLK_GPIO5] = imx_clk_hw_gate2("gpio5", "ipg", base + 0x6c, 30); + + /* CCGR2 */ + hws[IMX6SLL_CLK_GPIO6] = imx_clk_hw_gate2("gpio6", "ipg", base + 0x70, 0); + hws[IMX6SLL_CLK_CSI] = imx_clk_hw_gate2("csi", "axi", base + 0x70, 2); + hws[IMX6SLL_CLK_I2C1] = imx_clk_hw_gate2("i2c1", "perclk", base + 0x70, 6); + hws[IMX6SLL_CLK_I2C2] = imx_clk_hw_gate2("i2c2", "perclk", base + 0x70, 8); + hws[IMX6SLL_CLK_I2C3] = imx_clk_hw_gate2("i2c3", "perclk", base + 0x70, 10); + hws[IMX6SLL_CLK_OCOTP] = imx_clk_hw_gate2("ocotp", "ipg", base + 0x70, 12); + hws[IMX6SLL_CLK_GPIO3] = imx_clk_hw_gate2("gpio3", "ipg", base + 0x70, 26); + hws[IMX6SLL_CLK_LCDIF_APB] = imx_clk_hw_gate2("lcdif_apb", "axi", base + 0x70, 28); + hws[IMX6SLL_CLK_PXP] = imx_clk_hw_gate2("pxp", "axi", base + 0x70, 30); + + /* CCGR3 */ + hws[IMX6SLL_CLK_UART5_IPG] = imx_clk_hw_gate2("uart5_ipg", "ipg", base + 0x74, 2); + hws[IMX6SLL_CLK_UART5_SERIAL] = imx_clk_hw_gate2("uart5_serial", "uart_podf", base + 0x74, 2); + hws[IMX6SLL_CLK_EPDC_AXI] = imx_clk_hw_gate2("epdc_aclk", "axi", base + 0x74, 4); + hws[IMX6SLL_CLK_EPDC_PIX] = imx_clk_hw_gate2("epdc_pix", "epdc_podf", base + 0x74, 4); + hws[IMX6SLL_CLK_LCDIF_PIX] = imx_clk_hw_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10); + hws[IMX6SLL_CLK_GPIO4] = imx_clk_hw_gate2("gpio4", "ipg", base + 0x74, 12); + hws[IMX6SLL_CLK_WDOG1] = imx_clk_hw_gate2("wdog1", "ipg", base + 0x74, 16); + hws[IMX6SLL_CLK_MMDC_P0_FAST] = imx_clk_hw_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); + hws[IMX6SLL_CLK_MMDC_P0_IPG] = imx_clk_hw_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + hws[IMX6SLL_CLK_MMDC_P1_IPG] = imx_clk_hw_gate2_flags("mmdc_p1_ipg", "ipg", base + 0x74, 26, CLK_IS_CRITICAL); + hws[IMX6SLL_CLK_OCRAM] = imx_clk_hw_gate_flags("ocram", "ahb", base + 0x74, 28, CLK_IS_CRITICAL); + + /* CCGR4 */ + hws[IMX6SLL_CLK_PWM1] = imx_clk_hw_gate2("pwm1", "perclk", base + 0x78, 16); + hws[IMX6SLL_CLK_PWM2] = imx_clk_hw_gate2("pwm2", "perclk", base + 0x78, 18); + hws[IMX6SLL_CLK_PWM3] = imx_clk_hw_gate2("pwm3", "perclk", base + 0x78, 20); + hws[IMX6SLL_CLK_PWM4] = imx_clk_hw_gate2("pwm4", "perclk", base + 0x78, 22); + + /* CCGR5 */ + hws[IMX6SLL_CLK_ROM] = imx_clk_hw_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL); + hws[IMX6SLL_CLK_SDMA] = imx_clk_hw_gate2("sdma", "ahb", base + 0x7c, 6); + hws[IMX6SLL_CLK_WDOG2] = imx_clk_hw_gate2("wdog2", "ipg", base + 0x7c, 10); + hws[IMX6SLL_CLK_SPBA] = imx_clk_hw_gate2("spba", "ipg", base + 0x7c, 12); + hws[IMX6SLL_CLK_EXTERN_AUDIO] = imx_clk_hw_gate2_shared("extern_audio", "extern_audio_podf", base + 0x7c, 14, &share_count_audio); + hws[IMX6SLL_CLK_SPDIF] = imx_clk_hw_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio); + hws[IMX6SLL_CLK_SPDIF_GCLK] = imx_clk_hw_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_audio); + hws[IMX6SLL_CLK_SSI1] = imx_clk_hw_gate2_shared("ssi1", "ssi1_podf", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6SLL_CLK_SSI1_IPG] = imx_clk_hw_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6SLL_CLK_SSI2] = imx_clk_hw_gate2_shared("ssi2", "ssi2_podf", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6SLL_CLK_SSI2_IPG] = imx_clk_hw_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6SLL_CLK_SSI3] = imx_clk_hw_gate2_shared("ssi3", "ssi3_podf", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6SLL_CLK_SSI3_IPG] = imx_clk_hw_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6SLL_CLK_UART1_IPG] = imx_clk_hw_gate2("uart1_ipg", "ipg", base + 0x7c, 24); + hws[IMX6SLL_CLK_UART1_SERIAL] = imx_clk_hw_gate2("uart1_serial", "uart_podf", base + 0x7c, 24); + + /* CCGR6 */ + hws[IMX6SLL_CLK_USBOH3] = imx_clk_hw_gate2("usboh3", "ipg", base + 0x80, 0); + hws[IMX6SLL_CLK_USDHC1] = imx_clk_hw_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + hws[IMX6SLL_CLK_USDHC2] = imx_clk_hw_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + hws[IMX6SLL_CLK_USDHC3] = imx_clk_hw_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + + /* mask handshake of mmdc */ + imx_mmdc_mask_handshake(base, 0); + + imx_check_clk_hws(hws, IMX6SLL_CLK_END); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + + imx_register_uart_clocks(5); + + /* Lower the AHB clock rate before changing the clock source. */ + clk_set_rate(hws[IMX6SLL_CLK_AHB]->clk, 99000000); + + /* Change periph_pre clock to pll2_bus to adjust AXI rate to 264MHz */ + clk_set_parent(hws[IMX6SLL_CLK_PERIPH_CLK2_SEL]->clk, hws[IMX6SLL_CLK_PLL3_USB_OTG]->clk); + clk_set_parent(hws[IMX6SLL_CLK_PERIPH]->clk, hws[IMX6SLL_CLK_PERIPH_CLK2]->clk); + clk_set_parent(hws[IMX6SLL_CLK_PERIPH_PRE]->clk, hws[IMX6SLL_CLK_PLL2_BUS]->clk); + clk_set_parent(hws[IMX6SLL_CLK_PERIPH]->clk, hws[IMX6SLL_CLK_PERIPH_PRE]->clk); + + clk_set_rate(hws[IMX6SLL_CLK_AHB]->clk, 132000000); +} +CLK_OF_DECLARE_DRIVER(imx6sll, "fsl,imx6sll-ccm", imx6sll_clocks_init); diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c new file mode 100644 index 000000000..598f3cf4e --- /dev/null +++ b/drivers/clk/imx/clk-imx6sx.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. + */ + +#include <dt-bindings/clock/imx6sx-clock.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/types.h> + +#include "clk.h" + +static const char *step_sels[] = { "osc", "pll2_pfd2_396m", }; +static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +static const char *periph2_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll4_audio_div", }; +static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "osc", }; +static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "osc", }; +static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; +static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; +static const char *ocram_sels[] = { "periph", "pll2_pfd2_396m", "periph", "pll3_pfd1_540m", }; +static const char *audio_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", }; +static const char *gpu_axi_sels[] = { "pll2_pfd2_396m", "pll3_pfd0_720m", "pll3_pfd1_540m", "pll2_bus", }; +static const char *gpu_core_sels[] = { "pll3_pfd1_540m", "pll3_pfd0_720m", "pll2_bus", "pll2_pfd2_396m", }; +static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; +static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; +static const char *ldb_di0_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_pfd3_594m", "pll2_pfd1_594m", "pll3_pfd3_454m", }; +static const char *ldb_di1_sels[] = { "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_bus", "pll3_pfd3_454m", "pll3_pfd2_508m", }; +static const char *pcie_axi_sels[] = { "axi", "ahb", }; +static const char *ssi_sels[] = { "pll3_pfd2_508m", "pll5_video_div", "pll4_audio_div", }; +static const char *qspi1_sels[] = { "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_bus", "pll3_pfd3_454m", "pll3_pfd2_508m", }; +static const char *perclk_sels[] = { "ipg", "osc", }; +static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *vid_sels[] = { "pll3_pfd1_540m", "pll3_usb_otg", "pll3_pfd3_454m", "pll4_audio_div", "pll5_video_div", }; +static const char *can_sels[] = { "pll3_60m", "osc", "pll3_80m", "dummy", }; +static const char *uart_sels[] = { "pll3_80m", "osc", }; +static const char *qspi2_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", "pll3_pfd3_454m", "dummy", "dummy", "dummy", }; +static const char *enet_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", }; +static const char *enet_sels[] = { "enet_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *m4_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "osc", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd3_454m", }; +static const char *m4_sels[] = { "m4_pre_sel", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *eim_slow_sels[] = { "ocram", "pll3_usb_otg", "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char *lcdif1_pre_sels[] = { "pll2_bus", "pll3_pfd3_454m", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd1_594m", "pll3_pfd1_540m", }; +static const char *lcdif1_sels[] = { "lcdif1_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *lcdif2_pre_sels[] = { "pll2_bus", "pll3_pfd3_454m", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd3_594m", "pll3_pfd1_540m", }; +static const char *lcdif2_sels[] = { "lcdif2_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *display_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll3_usb_otg", "pll3_pfd1_540m", }; +static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; +static const char *cko1_sels[] = { + "dummy", "dummy", "dummy", "dummy", + "vadc", "ocram", "qspi2", "m4", "enet_ahb", "lcdif2_pix", + "lcdif1_pix", "ahb", "ipg", "perclk", "ckil", "pll4_audio_div", +}; +static const char *cko2_sels[] = { + "dummy", "mmdc_p0_fast", "usdhc4", "usdhc1", "dummy", "wrck", + "ecspi_root", "dummy", "usdhc3", "pcie", "arm", "csi_core", + "display_axi", "dummy", "osc", "dummy", "dummy", + "usdhc2", "ssi1", "ssi2", "ssi3", "gpu_axi_podf", "dummy", + "can_podf", "lvds1_out", "qspi1", "esai_extal", "eim_slow", + "uart_serial", "spdif", "audio", "dummy", +}; +static const char *cko_sels[] = { "cko1", "cko2", }; +static const char *lvds_sels[] = { + "arm", "pll1_sys", "dummy", "dummy", "dummy", "dummy", "dummy", "pll5_video_div", + "dummy", "dummy", "pcie_ref_125m", "dummy", "usbphy1", "usbphy2", +}; +static const char *pll_bypass_src_sels[] = { "osc", "lvds1_in", "lvds2_in", "dummy", }; +static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", }; +static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", }; +static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", }; +static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", }; +static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; +static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; +static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; + +static const struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, + { } +}; + +static const struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static const struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { } +}; + +static u32 share_count_asrc; +static u32 share_count_audio; +static u32 share_count_esai; +static u32 share_count_ssi1; +static u32 share_count_ssi2; +static u32 share_count_ssi3; +static u32 share_count_sai1; +static u32 share_count_sai2; + +static void __init imx6sx_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + IMX6SX_CLK_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return; + + clk_hw_data->num = IMX6SX_CLK_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX6SX_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + hws[IMX6SX_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); + hws[IMX6SX_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); + + /* ipp_di clock is external input */ + hws[IMX6SX_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0"); + hws[IMX6SX_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1"); + + /* Clock source from external clock via CLK1/2 PAD */ + hws[IMX6SX_CLK_ANACLK1] = imx_obtain_fixed_clk_hw(ccm_node, "anaclk1"); + hws[IMX6SX_CLK_ANACLK2] = imx_obtain_fixed_clk_hw(ccm_node, "anaclk2"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop"); + base = of_iomap(np, 0); + WARN_ON(!base); + of_node_put(np); + + hws[IMX6SX_PLL1_BYPASS_SRC] = imx_clk_hw_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SX_PLL2_BYPASS_SRC] = imx_clk_hw_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SX_PLL3_BYPASS_SRC] = imx_clk_hw_mux("pll3_bypass_src", base + 0x10, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SX_PLL4_BYPASS_SRC] = imx_clk_hw_mux("pll4_bypass_src", base + 0x70, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SX_PLL5_BYPASS_SRC] = imx_clk_hw_mux("pll5_bypass_src", base + 0xa0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SX_PLL6_BYPASS_SRC] = imx_clk_hw_mux("pll6_bypass_src", base + 0xe0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6SX_PLL7_BYPASS_SRC] = imx_clk_hw_mux("pll7_bypass_src", base + 0x20, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + + /* type name parent_name base div_mask */ + hws[IMX6SX_CLK_PLL1] = imx_clk_hw_pllv3(IMX_PLLV3_SYS, "pll1", "osc", base + 0x00, 0x7f); + hws[IMX6SX_CLK_PLL2] = imx_clk_hw_pllv3(IMX_PLLV3_GENERIC, "pll2", "osc", base + 0x30, 0x1); + hws[IMX6SX_CLK_PLL3] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll3", "osc", base + 0x10, 0x3); + hws[IMX6SX_CLK_PLL4] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll4", "osc", base + 0x70, 0x7f); + hws[IMX6SX_CLK_PLL5] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll5", "osc", base + 0xa0, 0x7f); + hws[IMX6SX_CLK_PLL6] = imx_clk_hw_pllv3(IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3); + hws[IMX6SX_CLK_PLL7] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll7", "osc", base + 0x20, 0x3); + + hws[IMX6SX_PLL1_BYPASS] = imx_clk_hw_mux_flags("pll1_bypass", base + 0x00, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_PLL2_BYPASS] = imx_clk_hw_mux_flags("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_PLL3_BYPASS] = imx_clk_hw_mux_flags("pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_PLL4_BYPASS] = imx_clk_hw_mux_flags("pll4_bypass", base + 0x70, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_PLL6_BYPASS] = imx_clk_hw_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_PLL7_BYPASS] = imx_clk_hw_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); + + /* Do not bypass PLLs initially */ + clk_set_parent(hws[IMX6SX_PLL1_BYPASS]->clk, hws[IMX6SX_CLK_PLL1]->clk); + clk_set_parent(hws[IMX6SX_PLL2_BYPASS]->clk, hws[IMX6SX_CLK_PLL2]->clk); + clk_set_parent(hws[IMX6SX_PLL3_BYPASS]->clk, hws[IMX6SX_CLK_PLL3]->clk); + clk_set_parent(hws[IMX6SX_PLL4_BYPASS]->clk, hws[IMX6SX_CLK_PLL4]->clk); + clk_set_parent(hws[IMX6SX_PLL5_BYPASS]->clk, hws[IMX6SX_CLK_PLL5]->clk); + clk_set_parent(hws[IMX6SX_PLL6_BYPASS]->clk, hws[IMX6SX_CLK_PLL6]->clk); + clk_set_parent(hws[IMX6SX_PLL7_BYPASS]->clk, hws[IMX6SX_CLK_PLL7]->clk); + + hws[IMX6SX_CLK_PLL1_SYS] = imx_clk_hw_gate("pll1_sys", "pll1_bypass", base + 0x00, 13); + hws[IMX6SX_CLK_PLL2_BUS] = imx_clk_hw_gate("pll2_bus", "pll2_bypass", base + 0x30, 13); + hws[IMX6SX_CLK_PLL3_USB_OTG] = imx_clk_hw_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13); + hws[IMX6SX_CLK_PLL4_AUDIO] = imx_clk_hw_gate("pll4_audio", "pll4_bypass", base + 0x70, 13); + hws[IMX6SX_CLK_PLL5_VIDEO] = imx_clk_hw_gate("pll5_video", "pll5_bypass", base + 0xa0, 13); + hws[IMX6SX_CLK_PLL6_ENET] = imx_clk_hw_gate("pll6_enet", "pll6_bypass", base + 0xe0, 13); + hws[IMX6SX_CLK_PLL7_USB_HOST] = imx_clk_hw_gate("pll7_usb_host", "pll7_bypass", base + 0x20, 13); + + /* + * Bit 20 is the reserved and read-only bit, we do this only for: + * - Do nothing for usbphy clk_enable/disable + * - Keep refcount when do usbphy clk_enable/disable, in that case, + * the clk framework may need to enable/disable usbphy's parent + */ + hws[IMX6SX_CLK_USBPHY1] = imx_clk_hw_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + hws[IMX6SX_CLK_USBPHY2] = imx_clk_hw_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + + /* + * usbphy*_gate needs to be on after system boots up, and software + * never needs to control it anymore. + */ + hws[IMX6SX_CLK_USBPHY1_GATE] = imx_clk_hw_gate("usbphy1_gate", "dummy", base + 0x10, 6); + hws[IMX6SX_CLK_USBPHY2_GATE] = imx_clk_hw_gate("usbphy2_gate", "dummy", base + 0x20, 6); + + /* FIXME 100MHz is used for pcie ref for all imx6 pcie, excepted imx6q */ + hws[IMX6SX_CLK_PCIE_REF] = imx_clk_hw_fixed_factor("pcie_ref", "pll6_enet", 1, 5); + hws[IMX6SX_CLK_PCIE_REF_125M] = imx_clk_hw_gate("pcie_ref_125m", "pcie_ref", base + 0xe0, 19); + + hws[IMX6SX_CLK_LVDS1_OUT] = imx_clk_hw_gate_exclusive("lvds1_out", "lvds1_sel", base + 0x160, 10, BIT(12)); + hws[IMX6SX_CLK_LVDS2_OUT] = imx_clk_hw_gate_exclusive("lvds2_out", "lvds2_sel", base + 0x160, 11, BIT(13)); + hws[IMX6SX_CLK_LVDS1_IN] = imx_clk_hw_gate_exclusive("lvds1_in", "anaclk1", base + 0x160, 12, BIT(10)); + hws[IMX6SX_CLK_LVDS2_IN] = imx_clk_hw_gate_exclusive("lvds2_in", "anaclk2", base + 0x160, 13, BIT(11)); + + hws[IMX6SX_CLK_ENET_REF] = clk_hw_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + base + 0xe0, 0, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + hws[IMX6SX_CLK_ENET2_REF] = clk_hw_register_divider_table(NULL, "enet2_ref", "pll6_enet", 0, + base + 0xe0, 2, 2, 0, clk_enet_ref_table, + &imx_ccm_lock); + hws[IMX6SX_CLK_ENET2_REF_125M] = imx_clk_hw_gate("enet2_ref_125m", "enet2_ref", base + 0xe0, 20); + + hws[IMX6SX_CLK_ENET_PTP_REF] = imx_clk_hw_fixed_factor("enet_ptp_ref", "pll6_enet", 1, 20); + hws[IMX6SX_CLK_ENET_PTP] = imx_clk_hw_gate("enet_ptp_25m", "enet_ptp_ref", base + 0xe0, 21); + + /* name parent_name reg idx */ + hws[IMX6SX_CLK_PLL2_PFD0] = imx_clk_hw_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); + hws[IMX6SX_CLK_PLL2_PFD1] = imx_clk_hw_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); + hws[IMX6SX_CLK_PLL2_PFD2] = imx_clk_hw_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); + hws[IMX6SX_CLK_PLL2_PFD3] = imx_clk_hw_pfd("pll2_pfd3_594m", "pll2_bus", base + 0x100, 3); + hws[IMX6SX_CLK_PLL3_PFD0] = imx_clk_hw_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); + hws[IMX6SX_CLK_PLL3_PFD1] = imx_clk_hw_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); + hws[IMX6SX_CLK_PLL3_PFD2] = imx_clk_hw_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); + hws[IMX6SX_CLK_PLL3_PFD3] = imx_clk_hw_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); + + /* name parent_name mult div */ + hws[IMX6SX_CLK_PLL2_198M] = imx_clk_hw_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); + hws[IMX6SX_CLK_PLL3_120M] = imx_clk_hw_fixed_factor("pll3_120m", "pll3_usb_otg", 1, 4); + hws[IMX6SX_CLK_PLL3_80M] = imx_clk_hw_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + hws[IMX6SX_CLK_PLL3_60M] = imx_clk_hw_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + hws[IMX6SX_CLK_TWD] = imx_clk_hw_fixed_factor("twd", "arm", 1, 2); + hws[IMX6SX_CLK_GPT_3M] = imx_clk_hw_fixed_factor("gpt_3m", "osc", 1, 8); + + hws[IMX6SX_CLK_PLL4_POST_DIV] = clk_hw_register_divider_table(NULL, "pll4_post_div", "pll4_audio", + CLK_SET_RATE_PARENT, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6SX_CLK_PLL4_AUDIO_DIV] = clk_hw_register_divider(NULL, "pll4_audio_div", "pll4_post_div", + CLK_SET_RATE_PARENT, base + 0x170, 15, 1, 0, &imx_ccm_lock); + hws[IMX6SX_CLK_PLL5_POST_DIV] = clk_hw_register_divider_table(NULL, "pll5_post_div", "pll5_video", + CLK_SET_RATE_PARENT, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6SX_CLK_PLL5_VIDEO_DIV] = clk_hw_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", + CLK_SET_RATE_PARENT, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + + /* name reg shift width parent_names num_parents */ + hws[IMX6SX_CLK_LVDS1_SEL] = imx_clk_hw_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + hws[IMX6SX_CLK_LVDS2_SEL] = imx_clk_hw_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels)); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + + /* name reg shift width parent_names num_parents */ + hws[IMX6SX_CLK_STEP] = imx_clk_hw_mux("step", base + 0xc, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + hws[IMX6SX_CLK_PLL1_SW] = imx_clk_hw_mux("pll1_sw", base + 0xc, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels)); + hws[IMX6SX_CLK_OCRAM_SEL] = imx_clk_hw_mux("ocram_sel", base + 0x14, 6, 2, ocram_sels, ARRAY_SIZE(ocram_sels)); + hws[IMX6SX_CLK_PERIPH_PRE] = imx_clk_hw_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + hws[IMX6SX_CLK_PERIPH2_PRE] = imx_clk_hw_mux("periph2_pre", base + 0x18, 21, 2, periph2_pre_sels, ARRAY_SIZE(periph2_pre_sels)); + hws[IMX6SX_CLK_PERIPH_CLK2_SEL] = imx_clk_hw_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + hws[IMX6SX_CLK_PERIPH2_CLK2_SEL] = imx_clk_hw_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + hws[IMX6SX_CLK_PCIE_AXI_SEL] = imx_clk_hw_mux("pcie_axi_sel", base + 0x18, 10, 1, pcie_axi_sels, ARRAY_SIZE(pcie_axi_sels)); + hws[IMX6SX_CLK_GPU_AXI_SEL] = imx_clk_hw_mux("gpu_axi_sel", base + 0x18, 8, 2, gpu_axi_sels, ARRAY_SIZE(gpu_axi_sels)); + hws[IMX6SX_CLK_GPU_CORE_SEL] = imx_clk_hw_mux("gpu_core_sel", base + 0x18, 4, 2, gpu_core_sels, ARRAY_SIZE(gpu_core_sels)); + hws[IMX6SX_CLK_EIM_SLOW_SEL] = imx_clk_hw_mux("eim_slow_sel", base + 0x1c, 29, 2, eim_slow_sels, ARRAY_SIZE(eim_slow_sels)); + hws[IMX6SX_CLK_USDHC1_SEL] = imx_clk_hw_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6SX_CLK_USDHC2_SEL] = imx_clk_hw_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6SX_CLK_USDHC3_SEL] = imx_clk_hw_mux("usdhc3_sel", base + 0x1c, 18, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6SX_CLK_USDHC4_SEL] = imx_clk_hw_mux("usdhc4_sel", base + 0x1c, 19, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6SX_CLK_SSI3_SEL] = imx_clk_hw_mux("ssi3_sel", base + 0x1c, 14, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6SX_CLK_SSI2_SEL] = imx_clk_hw_mux("ssi2_sel", base + 0x1c, 12, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6SX_CLK_SSI1_SEL] = imx_clk_hw_mux("ssi1_sel", base + 0x1c, 10, 2, ssi_sels, ARRAY_SIZE(ssi_sels)); + hws[IMX6SX_CLK_QSPI1_SEL] = imx_clk_hw_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels)); + hws[IMX6SX_CLK_PERCLK_SEL] = imx_clk_hw_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); + hws[IMX6SX_CLK_VID_SEL] = imx_clk_hw_mux("vid_sel", base + 0x20, 21, 3, vid_sels, ARRAY_SIZE(vid_sels)); + hws[IMX6SX_CLK_ESAI_SEL] = imx_clk_hw_mux("esai_sel", base + 0x20, 19, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6SX_CLK_CAN_SEL] = imx_clk_hw_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); + hws[IMX6SX_CLK_UART_SEL] = imx_clk_hw_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + hws[IMX6SX_CLK_QSPI2_SEL] = imx_clk_hw_mux("qspi2_sel", base + 0x2c, 15, 3, qspi2_sels, ARRAY_SIZE(qspi2_sels)); + hws[IMX6SX_CLK_SPDIF_SEL] = imx_clk_hw_mux("spdif_sel", base + 0x30, 20, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6SX_CLK_AUDIO_SEL] = imx_clk_hw_mux("audio_sel", base + 0x30, 7, 2, audio_sels, ARRAY_SIZE(audio_sels)); + hws[IMX6SX_CLK_ENET_PRE_SEL] = imx_clk_hw_mux("enet_pre_sel", base + 0x34, 15, 3, enet_pre_sels, ARRAY_SIZE(enet_pre_sels)); + hws[IMX6SX_CLK_ENET_SEL] = imx_clk_hw_mux("enet_sel", base + 0x34, 9, 3, enet_sels, ARRAY_SIZE(enet_sels)); + hws[IMX6SX_CLK_M4_PRE_SEL] = imx_clk_hw_mux("m4_pre_sel", base + 0x34, 6, 3, m4_pre_sels, ARRAY_SIZE(m4_pre_sels)); + hws[IMX6SX_CLK_M4_SEL] = imx_clk_hw_mux("m4_sel", base + 0x34, 0, 3, m4_sels, ARRAY_SIZE(m4_sels)); + hws[IMX6SX_CLK_ECSPI_SEL] = imx_clk_hw_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + hws[IMX6SX_CLK_LCDIF2_PRE_SEL] = imx_clk_hw_mux("lcdif2_pre_sel", base + 0x38, 6, 3, lcdif2_pre_sels, ARRAY_SIZE(lcdif2_pre_sels)); + hws[IMX6SX_CLK_LCDIF2_SEL] = imx_clk_hw_mux("lcdif2_sel", base + 0x38, 0, 3, lcdif2_sels, ARRAY_SIZE(lcdif2_sels)); + hws[IMX6SX_CLK_DISPLAY_SEL] = imx_clk_hw_mux("display_sel", base + 0x3c, 14, 2, display_sels, ARRAY_SIZE(display_sels)); + hws[IMX6SX_CLK_CSI_SEL] = imx_clk_hw_mux("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels)); + hws[IMX6SX_CLK_CKO1_SEL] = imx_clk_hw_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); + hws[IMX6SX_CLK_CKO2_SEL] = imx_clk_hw_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); + hws[IMX6SX_CLK_CKO] = imx_clk_hw_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); + + hws[IMX6SX_CLK_LDB_DI1_DIV_SEL] = imx_clk_hw_mux_flags("ldb_di1_div_sel", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_CLK_LDB_DI0_DIV_SEL] = imx_clk_hw_mux_flags("ldb_di0_div_sel", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_CLK_LDB_DI1_SEL] = imx_clk_hw_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di1_sels, ARRAY_SIZE(ldb_di1_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_CLK_LDB_DI0_SEL] = imx_clk_hw_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_CLK_LCDIF1_PRE_SEL] = imx_clk_hw_mux_flags("lcdif1_pre_sel", base + 0x38, 15, 3, lcdif1_pre_sels, ARRAY_SIZE(lcdif1_pre_sels), CLK_SET_RATE_PARENT); + hws[IMX6SX_CLK_LCDIF1_SEL] = imx_clk_hw_mux_flags("lcdif1_sel", base + 0x38, 9, 3, lcdif1_sels, ARRAY_SIZE(lcdif1_sels), CLK_SET_RATE_PARENT); + + /* name parent_name reg shift width */ + hws[IMX6SX_CLK_PERIPH_CLK2] = imx_clk_hw_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); + hws[IMX6SX_CLK_PERIPH2_CLK2] = imx_clk_hw_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + hws[IMX6SX_CLK_IPG] = imx_clk_hw_divider("ipg", "ahb", base + 0x14, 8, 2); + hws[IMX6SX_CLK_GPU_CORE_PODF] = imx_clk_hw_divider("gpu_core_podf", "gpu_core_sel", base + 0x18, 29, 3); + hws[IMX6SX_CLK_GPU_AXI_PODF] = imx_clk_hw_divider("gpu_axi_podf", "gpu_axi_sel", base + 0x18, 26, 3); + hws[IMX6SX_CLK_LCDIF1_PODF] = imx_clk_hw_divider("lcdif1_podf", "lcdif1_pred", base + 0x18, 23, 3); + hws[IMX6SX_CLK_QSPI1_PODF] = imx_clk_hw_divider("qspi1_podf", "qspi1_sel", base + 0x1c, 26, 3); + hws[IMX6SX_CLK_EIM_SLOW_PODF] = imx_clk_hw_divider("eim_slow_podf", "eim_slow_sel", base + 0x1c, 23, 3); + hws[IMX6SX_CLK_LCDIF2_PODF] = imx_clk_hw_divider("lcdif2_podf", "lcdif2_pred", base + 0x1c, 20, 3); + hws[IMX6SX_CLK_PERCLK] = imx_clk_hw_divider_flags("perclk", "perclk_sel", base + 0x1c, 0, 6, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_VID_PODF] = imx_clk_hw_divider("vid_podf", "vid_sel", base + 0x20, 24, 2); + hws[IMX6SX_CLK_CAN_PODF] = imx_clk_hw_divider("can_podf", "can_sel", base + 0x20, 2, 6); + hws[IMX6SX_CLK_USDHC4_PODF] = imx_clk_hw_divider("usdhc4_podf", "usdhc4_sel", base + 0x24, 22, 3); + hws[IMX6SX_CLK_USDHC3_PODF] = imx_clk_hw_divider("usdhc3_podf", "usdhc3_sel", base + 0x24, 19, 3); + hws[IMX6SX_CLK_USDHC2_PODF] = imx_clk_hw_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + hws[IMX6SX_CLK_USDHC1_PODF] = imx_clk_hw_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + hws[IMX6SX_CLK_UART_PODF] = imx_clk_hw_divider("uart_podf", "uart_sel", base + 0x24, 0, 6); + hws[IMX6SX_CLK_ESAI_PRED] = imx_clk_hw_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); + hws[IMX6SX_CLK_ESAI_PODF] = imx_clk_hw_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); + hws[IMX6SX_CLK_SSI3_PRED] = imx_clk_hw_divider("ssi3_pred", "ssi3_sel", base + 0x28, 22, 3); + hws[IMX6SX_CLK_SSI3_PODF] = imx_clk_hw_divider("ssi3_podf", "ssi3_pred", base + 0x28, 16, 6); + hws[IMX6SX_CLK_SSI1_PRED] = imx_clk_hw_divider("ssi1_pred", "ssi1_sel", base + 0x28, 6, 3); + hws[IMX6SX_CLK_SSI1_PODF] = imx_clk_hw_divider("ssi1_podf", "ssi1_pred", base + 0x28, 0, 6); + hws[IMX6SX_CLK_QSPI2_PRED] = imx_clk_hw_divider("qspi2_pred", "qspi2_sel", base + 0x2c, 18, 3); + hws[IMX6SX_CLK_QSPI2_PODF] = imx_clk_hw_divider("qspi2_podf", "qspi2_pred", base + 0x2c, 21, 6); + hws[IMX6SX_CLK_SSI2_PRED] = imx_clk_hw_divider("ssi2_pred", "ssi2_sel", base + 0x2c, 6, 3); + hws[IMX6SX_CLK_SSI2_PODF] = imx_clk_hw_divider("ssi2_podf", "ssi2_pred", base + 0x2c, 0, 6); + hws[IMX6SX_CLK_SPDIF_PRED] = imx_clk_hw_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); + hws[IMX6SX_CLK_SPDIF_PODF] = imx_clk_hw_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); + hws[IMX6SX_CLK_AUDIO_PRED] = imx_clk_hw_divider("audio_pred", "audio_sel", base + 0x30, 12, 3); + hws[IMX6SX_CLK_AUDIO_PODF] = imx_clk_hw_divider("audio_podf", "audio_pred", base + 0x30, 9, 3); + hws[IMX6SX_CLK_ENET_PODF] = imx_clk_hw_divider("enet_podf", "enet_pre_sel", base + 0x34, 12, 3); + hws[IMX6SX_CLK_M4_PODF] = imx_clk_hw_divider("m4_podf", "m4_sel", base + 0x34, 3, 3); + hws[IMX6SX_CLK_ECSPI_PODF] = imx_clk_hw_divider("ecspi_podf", "ecspi_sel", base + 0x38, 19, 6); + hws[IMX6SX_CLK_LCDIF1_PRED] = imx_clk_hw_divider("lcdif1_pred", "lcdif1_pre_sel", base + 0x38, 12, 3); + hws[IMX6SX_CLK_LCDIF2_PRED] = imx_clk_hw_divider("lcdif2_pred", "lcdif2_pre_sel", base + 0x38, 3, 3); + hws[IMX6SX_CLK_DISPLAY_PODF] = imx_clk_hw_divider("display_podf", "display_sel", base + 0x3c, 16, 3); + hws[IMX6SX_CLK_CSI_PODF] = imx_clk_hw_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); + hws[IMX6SX_CLK_CKO1_PODF] = imx_clk_hw_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); + hws[IMX6SX_CLK_CKO2_PODF] = imx_clk_hw_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); + + hws[IMX6SX_CLK_LDB_DI0_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); + hws[IMX6SX_CLK_LDB_DI0_DIV_7] = imx_clk_hw_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); + hws[IMX6SX_CLK_LDB_DI1_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7); + hws[IMX6SX_CLK_LDB_DI1_DIV_7] = imx_clk_hw_fixed_factor("ldb_di1_div_7", "ldb_di1_sel", 1, 7); + + /* name reg shift width busy: reg, shift parent_names num_parents */ + hws[IMX6SX_CLK_PERIPH] = imx_clk_hw_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + hws[IMX6SX_CLK_PERIPH2] = imx_clk_hw_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + /* name parent_name reg shift width busy: reg, shift */ + hws[IMX6SX_CLK_OCRAM_PODF] = imx_clk_hw_busy_divider("ocram_podf", "ocram_sel", base + 0x14, 16, 3, base + 0x48, 0); + hws[IMX6SX_CLK_AHB] = imx_clk_hw_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + hws[IMX6SX_CLK_MMDC_PODF] = imx_clk_hw_busy_divider("mmdc_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + hws[IMX6SX_CLK_ARM] = imx_clk_hw_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + + /* name parent_name reg shift */ + /* CCGR0 */ + hws[IMX6SX_CLK_AIPS_TZ1] = imx_clk_hw_gate2_flags("aips_tz1", "ahb", base + 0x68, 0, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_AIPS_TZ2] = imx_clk_hw_gate2_flags("aips_tz2", "ahb", base + 0x68, 2, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_APBH_DMA] = imx_clk_hw_gate2("apbh_dma", "usdhc3", base + 0x68, 4); + hws[IMX6SX_CLK_ASRC_MEM] = imx_clk_hw_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); + hws[IMX6SX_CLK_ASRC_IPG] = imx_clk_hw_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); + hws[IMX6SX_CLK_CAAM_MEM] = imx_clk_hw_gate2("caam_mem", "ahb", base + 0x68, 8); + hws[IMX6SX_CLK_CAAM_ACLK] = imx_clk_hw_gate2("caam_aclk", "ahb", base + 0x68, 10); + hws[IMX6SX_CLK_CAAM_IPG] = imx_clk_hw_gate2("caam_ipg", "ipg", base + 0x68, 12); + hws[IMX6SX_CLK_CAN1_IPG] = imx_clk_hw_gate2("can1_ipg", "ipg", base + 0x68, 14); + hws[IMX6SX_CLK_CAN1_SERIAL] = imx_clk_hw_gate2("can1_serial", "can_podf", base + 0x68, 16); + hws[IMX6SX_CLK_CAN2_IPG] = imx_clk_hw_gate2("can2_ipg", "ipg", base + 0x68, 18); + hws[IMX6SX_CLK_CAN2_SERIAL] = imx_clk_hw_gate2("can2_serial", "can_podf", base + 0x68, 20); + hws[IMX6SX_CLK_DCIC1] = imx_clk_hw_gate2("dcic1", "display_podf", base + 0x68, 24); + hws[IMX6SX_CLK_DCIC2] = imx_clk_hw_gate2("dcic2", "display_podf", base + 0x68, 26); + hws[IMX6SX_CLK_AIPS_TZ3] = imx_clk_hw_gate2_flags("aips_tz3", "ahb", base + 0x68, 30, CLK_IS_CRITICAL); + + /* CCGR1 */ + hws[IMX6SX_CLK_ECSPI1] = imx_clk_hw_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); + hws[IMX6SX_CLK_ECSPI2] = imx_clk_hw_gate2("ecspi2", "ecspi_podf", base + 0x6c, 2); + hws[IMX6SX_CLK_ECSPI3] = imx_clk_hw_gate2("ecspi3", "ecspi_podf", base + 0x6c, 4); + hws[IMX6SX_CLK_ECSPI4] = imx_clk_hw_gate2("ecspi4", "ecspi_podf", base + 0x6c, 6); + hws[IMX6SX_CLK_ECSPI5] = imx_clk_hw_gate2("ecspi5", "ecspi_podf", base + 0x6c, 8); + hws[IMX6SX_CLK_EPIT1] = imx_clk_hw_gate2("epit1", "perclk", base + 0x6c, 12); + hws[IMX6SX_CLK_EPIT2] = imx_clk_hw_gate2("epit2", "perclk", base + 0x6c, 14); + hws[IMX6SX_CLK_ESAI_EXTAL] = imx_clk_hw_gate2_shared("esai_extal", "esai_podf", base + 0x6c, 16, &share_count_esai); + hws[IMX6SX_CLK_ESAI_IPG] = imx_clk_hw_gate2_shared("esai_ipg", "ahb", base + 0x6c, 16, &share_count_esai); + hws[IMX6SX_CLK_ESAI_MEM] = imx_clk_hw_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai); + hws[IMX6SX_CLK_WAKEUP] = imx_clk_hw_gate2_flags("wakeup", "ipg", base + 0x6c, 18, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_GPT_BUS] = imx_clk_hw_gate2("gpt_bus", "perclk", base + 0x6c, 20); + hws[IMX6SX_CLK_GPT_SERIAL] = imx_clk_hw_gate2("gpt_serial", "perclk", base + 0x6c, 22); + hws[IMX6SX_CLK_GPU] = imx_clk_hw_gate2("gpu", "gpu_core_podf", base + 0x6c, 26); + hws[IMX6SX_CLK_OCRAM_S] = imx_clk_hw_gate2("ocram_s", "ahb", base + 0x6c, 28); + hws[IMX6SX_CLK_CANFD] = imx_clk_hw_gate2("canfd", "can_podf", base + 0x6c, 30); + + /* CCGR2 */ + hws[IMX6SX_CLK_CSI] = imx_clk_hw_gate2("csi", "csi_podf", base + 0x70, 2); + hws[IMX6SX_CLK_I2C1] = imx_clk_hw_gate2("i2c1", "perclk", base + 0x70, 6); + hws[IMX6SX_CLK_I2C2] = imx_clk_hw_gate2("i2c2", "perclk", base + 0x70, 8); + hws[IMX6SX_CLK_I2C3] = imx_clk_hw_gate2("i2c3", "perclk", base + 0x70, 10); + hws[IMX6SX_CLK_OCOTP] = imx_clk_hw_gate2("ocotp", "ipg", base + 0x70, 12); + hws[IMX6SX_CLK_IOMUXC] = imx_clk_hw_gate2("iomuxc", "lcdif1_podf", base + 0x70, 14); + hws[IMX6SX_CLK_IPMUX1] = imx_clk_hw_gate2_flags("ipmux1", "ahb", base + 0x70, 16, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_IPMUX2] = imx_clk_hw_gate2_flags("ipmux2", "ahb", base + 0x70, 18, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_IPMUX3] = imx_clk_hw_gate2_flags("ipmux3", "ahb", base + 0x70, 20, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_TZASC1] = imx_clk_hw_gate2_flags("tzasc1", "mmdc_podf", base + 0x70, 22, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_LCDIF_APB] = imx_clk_hw_gate2("lcdif_apb", "display_podf", base + 0x70, 28); + hws[IMX6SX_CLK_PXP_AXI] = imx_clk_hw_gate2("pxp_axi", "display_podf", base + 0x70, 30); + + /* CCGR3 */ + hws[IMX6SX_CLK_M4] = imx_clk_hw_gate2("m4", "m4_podf", base + 0x74, 2); + hws[IMX6SX_CLK_ENET] = imx_clk_hw_gate2("enet", "ipg", base + 0x74, 4); + hws[IMX6SX_CLK_ENET_AHB] = imx_clk_hw_gate2("enet_ahb", "enet_sel", base + 0x74, 4); + hws[IMX6SX_CLK_DISPLAY_AXI] = imx_clk_hw_gate2("display_axi", "display_podf", base + 0x74, 6); + hws[IMX6SX_CLK_LCDIF2_PIX] = imx_clk_hw_gate2("lcdif2_pix", "lcdif2_sel", base + 0x74, 8); + hws[IMX6SX_CLK_LCDIF1_PIX] = imx_clk_hw_gate2("lcdif1_pix", "lcdif1_sel", base + 0x74, 10); + hws[IMX6SX_CLK_LDB_DI0] = imx_clk_hw_gate2("ldb_di0", "ldb_di0_div_sel", base + 0x74, 12); + hws[IMX6SX_CLK_QSPI1] = imx_clk_hw_gate2("qspi1", "qspi1_podf", base + 0x74, 14); + hws[IMX6SX_CLK_MLB] = imx_clk_hw_gate2("mlb", "ahb", base + 0x74, 18); + hws[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_hw_gate2_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_hw_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_MMDC_P1_IPG] = imx_clk_hw_gate2_flags("mmdc_p1_ipg", "ipg", base + 0x74, 26, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_OCRAM] = imx_clk_hw_gate2_flags("ocram", "ocram_podf", base + 0x74, 28, CLK_IS_CRITICAL); + + /* CCGR4 */ + hws[IMX6SX_CLK_PCIE_AXI] = imx_clk_hw_gate2("pcie_axi", "display_podf", base + 0x78, 0); + hws[IMX6SX_CLK_QSPI2] = imx_clk_hw_gate2("qspi2", "qspi2_podf", base + 0x78, 10); + hws[IMX6SX_CLK_PER1_BCH] = imx_clk_hw_gate2("per1_bch", "usdhc3", base + 0x78, 12); + hws[IMX6SX_CLK_PER2_MAIN] = imx_clk_hw_gate2_flags("per2_main", "ahb", base + 0x78, 14, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_PWM1] = imx_clk_hw_gate2("pwm1", "perclk", base + 0x78, 16); + hws[IMX6SX_CLK_PWM2] = imx_clk_hw_gate2("pwm2", "perclk", base + 0x78, 18); + hws[IMX6SX_CLK_PWM3] = imx_clk_hw_gate2("pwm3", "perclk", base + 0x78, 20); + hws[IMX6SX_CLK_PWM4] = imx_clk_hw_gate2("pwm4", "perclk", base + 0x78, 22); + hws[IMX6SX_CLK_GPMI_BCH_APB] = imx_clk_hw_gate2("gpmi_bch_apb", "usdhc3", base + 0x78, 24); + hws[IMX6SX_CLK_GPMI_BCH] = imx_clk_hw_gate2("gpmi_bch", "usdhc4", base + 0x78, 26); + hws[IMX6SX_CLK_GPMI_IO] = imx_clk_hw_gate2("gpmi_io", "qspi2_podf", base + 0x78, 28); + hws[IMX6SX_CLK_GPMI_APB] = imx_clk_hw_gate2("gpmi_apb", "usdhc3", base + 0x78, 30); + + /* CCGR5 */ + hws[IMX6SX_CLK_ROM] = imx_clk_hw_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL); + hws[IMX6SX_CLK_SDMA] = imx_clk_hw_gate2("sdma", "ahb", base + 0x7c, 6); + hws[IMX6SX_CLK_SPBA] = imx_clk_hw_gate2("spba", "ipg", base + 0x7c, 12); + hws[IMX6SX_CLK_AUDIO] = imx_clk_hw_gate2_shared("audio", "audio_podf", base + 0x7c, 14, &share_count_audio); + hws[IMX6SX_CLK_SPDIF] = imx_clk_hw_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio); + hws[IMX6SX_CLK_SPDIF_GCLK] = imx_clk_hw_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_audio); + hws[IMX6SX_CLK_SSI1_IPG] = imx_clk_hw_gate2_shared("ssi1_ipg", "ipg", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6SX_CLK_SSI2_IPG] = imx_clk_hw_gate2_shared("ssi2_ipg", "ipg", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6SX_CLK_SSI3_IPG] = imx_clk_hw_gate2_shared("ssi3_ipg", "ipg", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6SX_CLK_SSI1] = imx_clk_hw_gate2_shared("ssi1", "ssi1_podf", base + 0x7c, 18, &share_count_ssi1); + hws[IMX6SX_CLK_SSI2] = imx_clk_hw_gate2_shared("ssi2", "ssi2_podf", base + 0x7c, 20, &share_count_ssi2); + hws[IMX6SX_CLK_SSI3] = imx_clk_hw_gate2_shared("ssi3", "ssi3_podf", base + 0x7c, 22, &share_count_ssi3); + hws[IMX6SX_CLK_UART_IPG] = imx_clk_hw_gate2("uart_ipg", "ipg", base + 0x7c, 24); + hws[IMX6SX_CLK_UART_SERIAL] = imx_clk_hw_gate2("uart_serial", "uart_podf", base + 0x7c, 26); + hws[IMX6SX_CLK_SAI1_IPG] = imx_clk_hw_gate2_shared("sai1_ipg", "ipg", base + 0x7c, 28, &share_count_sai1); + hws[IMX6SX_CLK_SAI2_IPG] = imx_clk_hw_gate2_shared("sai2_ipg", "ipg", base + 0x7c, 30, &share_count_sai2); + hws[IMX6SX_CLK_SAI1] = imx_clk_hw_gate2_shared("sai1", "ssi1_podf", base + 0x7c, 28, &share_count_sai1); + hws[IMX6SX_CLK_SAI2] = imx_clk_hw_gate2_shared("sai2", "ssi2_podf", base + 0x7c, 30, &share_count_sai2); + + /* CCGR6 */ + hws[IMX6SX_CLK_USBOH3] = imx_clk_hw_gate2("usboh3", "ipg", base + 0x80, 0); + hws[IMX6SX_CLK_USDHC1] = imx_clk_hw_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + hws[IMX6SX_CLK_USDHC2] = imx_clk_hw_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + hws[IMX6SX_CLK_USDHC3] = imx_clk_hw_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6); + hws[IMX6SX_CLK_USDHC4] = imx_clk_hw_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8); + hws[IMX6SX_CLK_EIM_SLOW] = imx_clk_hw_gate2("eim_slow", "eim_slow_podf", base + 0x80, 10); + hws[IMX6SX_CLK_PWM8] = imx_clk_hw_gate2("pwm8", "perclk", base + 0x80, 16); + hws[IMX6SX_CLK_VADC] = imx_clk_hw_gate2("vadc", "vid_podf", base + 0x80, 20); + hws[IMX6SX_CLK_GIS] = imx_clk_hw_gate2("gis", "display_podf", base + 0x80, 22); + hws[IMX6SX_CLK_I2C4] = imx_clk_hw_gate2("i2c4", "perclk", base + 0x80, 24); + hws[IMX6SX_CLK_PWM5] = imx_clk_hw_gate2("pwm5", "perclk", base + 0x80, 26); + hws[IMX6SX_CLK_PWM6] = imx_clk_hw_gate2("pwm6", "perclk", base + 0x80, 28); + hws[IMX6SX_CLK_PWM7] = imx_clk_hw_gate2("pwm7", "perclk", base + 0x80, 30); + + hws[IMX6SX_CLK_CKO1] = imx_clk_hw_gate("cko1", "cko1_podf", base + 0x60, 7); + hws[IMX6SX_CLK_CKO2] = imx_clk_hw_gate("cko2", "cko2_podf", base + 0x60, 24); + + /* mask handshake of mmdc */ + imx_mmdc_mask_handshake(base, 0); + + imx_check_clk_hws(hws, IMX6SX_CLK_CLK_END); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(hws[IMX6SX_CLK_USBPHY1_GATE]->clk); + clk_prepare_enable(hws[IMX6SX_CLK_USBPHY2_GATE]->clk); + } + + /* Set the default 132MHz for EIM module */ + clk_set_parent(hws[IMX6SX_CLK_EIM_SLOW_SEL]->clk, hws[IMX6SX_CLK_PLL2_PFD2]->clk); + clk_set_rate(hws[IMX6SX_CLK_EIM_SLOW]->clk, 132000000); + + /* set parent clock for LCDIF1 pixel clock */ + clk_set_parent(hws[IMX6SX_CLK_LCDIF1_PRE_SEL]->clk, hws[IMX6SX_CLK_PLL5_VIDEO_DIV]->clk); + clk_set_parent(hws[IMX6SX_CLK_LCDIF1_SEL]->clk, hws[IMX6SX_CLK_LCDIF1_PODF]->clk); + + /* Set the parent clks of PCIe lvds1 and pcie_axi to be pcie ref, axi */ + if (clk_set_parent(hws[IMX6SX_CLK_LVDS1_SEL]->clk, hws[IMX6SX_CLK_PCIE_REF_125M]->clk)) + pr_err("Failed to set pcie bus parent clk.\n"); + + /* + * Init enet system AHB clock, set to 200MHz + * pll2_pfd2_396m-> ENET_PODF-> ENET_AHB + */ + clk_set_parent(hws[IMX6SX_CLK_ENET_PRE_SEL]->clk, hws[IMX6SX_CLK_PLL2_PFD2]->clk); + clk_set_parent(hws[IMX6SX_CLK_ENET_SEL]->clk, hws[IMX6SX_CLK_ENET_PODF]->clk); + clk_set_rate(hws[IMX6SX_CLK_ENET_PODF]->clk, 200000000); + clk_set_rate(hws[IMX6SX_CLK_ENET_REF]->clk, 125000000); + clk_set_rate(hws[IMX6SX_CLK_ENET2_REF]->clk, 125000000); + + /* Audio clocks */ + clk_set_rate(hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk, 393216000); + + clk_set_parent(hws[IMX6SX_CLK_SPDIF_SEL]->clk, hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk); + clk_set_rate(hws[IMX6SX_CLK_SPDIF_PODF]->clk, 98304000); + + clk_set_parent(hws[IMX6SX_CLK_AUDIO_SEL]->clk, hws[IMX6SX_CLK_PLL3_USB_OTG]->clk); + clk_set_rate(hws[IMX6SX_CLK_AUDIO_PODF]->clk, 24000000); + + clk_set_parent(hws[IMX6SX_CLK_SSI1_SEL]->clk, hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk); + clk_set_parent(hws[IMX6SX_CLK_SSI2_SEL]->clk, hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk); + clk_set_parent(hws[IMX6SX_CLK_SSI3_SEL]->clk, hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk); + clk_set_rate(hws[IMX6SX_CLK_SSI1_PODF]->clk, 24576000); + clk_set_rate(hws[IMX6SX_CLK_SSI2_PODF]->clk, 24576000); + clk_set_rate(hws[IMX6SX_CLK_SSI3_PODF]->clk, 24576000); + + clk_set_parent(hws[IMX6SX_CLK_ESAI_SEL]->clk, hws[IMX6SX_CLK_PLL4_AUDIO_DIV]->clk); + clk_set_rate(hws[IMX6SX_CLK_ESAI_PODF]->clk, 24576000); + + /* Set parent clock for vadc */ + clk_set_parent(hws[IMX6SX_CLK_VID_SEL]->clk, hws[IMX6SX_CLK_PLL3_USB_OTG]->clk); + + /* default parent of can_sel clock is invalid, manually set it here */ + clk_set_parent(hws[IMX6SX_CLK_CAN_SEL]->clk, hws[IMX6SX_CLK_PLL3_60M]->clk); + + /* Update gpu clock from default 528M to 720M */ + clk_set_parent(hws[IMX6SX_CLK_GPU_CORE_SEL]->clk, hws[IMX6SX_CLK_PLL3_PFD0]->clk); + clk_set_parent(hws[IMX6SX_CLK_GPU_AXI_SEL]->clk, hws[IMX6SX_CLK_PLL3_PFD0]->clk); + + clk_set_parent(hws[IMX6SX_CLK_QSPI1_SEL]->clk, hws[IMX6SX_CLK_PLL2_BUS]->clk); + clk_set_parent(hws[IMX6SX_CLK_QSPI2_SEL]->clk, hws[IMX6SX_CLK_PLL2_BUS]->clk); + + imx_register_uart_clocks(2); +} +CLK_OF_DECLARE(imx6sx, "fsl,imx6sx-ccm", imx6sx_clocks_init); diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c new file mode 100644 index 000000000..520b100bf --- /dev/null +++ b/drivers/clk/imx/clk-imx6ul.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + */ + +#include <dt-bindings/clock/imx6ul-clock.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/types.h> + +#include "clk.h" + +static const char *pll_bypass_src_sels[] = { "osc", "dummy", }; +static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", }; +static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", }; +static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", }; +static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", }; +static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; +static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; +static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; +static const char *ca7_secondary_sels[] = { "pll2_pfd2_396m", "pll2_bus", }; +static const char *step_sels[] = { "osc", "ca7_secondary_sel", }; +static const char *pll1_sw_sels[] = { "pll1_sys", "step", }; +static const char *axi_alt_sels[] = { "pll2_pfd2_396m", "pll3_pfd1_540m", }; +static const char *axi_sels[] = {"periph", "axi_alt_sel", }; +static const char *periph_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll2_198m", }; +static const char *periph2_pre_sels[] = { "pll2_bus", "pll2_pfd2_396m", "pll2_pfd0_352m", "pll4_audio_div", }; +static const char *periph_clk2_sels[] = { "pll3_usb_otg", "osc", "pll2_bypass_src", }; +static const char *periph2_clk2_sels[] = { "pll3_usb_otg", "osc", }; +static const char *periph_sels[] = { "periph_pre", "periph_clk2", }; +static const char *periph2_sels[] = { "periph2_pre", "periph2_clk2", }; +static const char *usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *bch_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *gpmi_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *eim_slow_sels[] = { "axi", "pll3_usb_otg", "pll2_pfd2_396m", "pll3_pfd0_720m", }; +static const char *spdif_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", }; +static const char *sai_sels[] = { "pll3_pfd2_508m", "pll5_video_div", "pll4_audio_div", }; +static const char *lcdif_pre_sels[] = { "pll2_bus", "pll3_pfd3_454m", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd1_594m", "pll3_pfd1_540m", }; +static const char *sim_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", }; +static const char *ldb_di0_sels[] = { "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_pfd3_594m", "pll2_pfd1_594m", "pll3_pfd3_454m", }; +static const char *ldb_di0_div_sels[] = { "ldb_di0_div_3_5", "ldb_di0_div_7", }; +static const char *ldb_di1_div_sels[] = { "ldb_di1_div_3_5", "ldb_di1_div_7", }; +static const char *qspi1_sels[] = { "pll3_usb_otg", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll2_bus", "pll3_pfd3_454m", "pll3_pfd2_508m", }; +static const char *enfc_sels[] = { "pll2_pfd0_352m", "pll2_bus", "pll3_usb_otg", "pll2_pfd2_396m", "pll3_pfd3_454m", "dummy", "dummy", "dummy", }; +static const char *can_sels[] = { "pll3_60m", "osc", "pll3_80m", "dummy", }; +static const char *ecspi_sels[] = { "pll3_60m", "osc", }; +static const char *uart_sels[] = { "pll3_80m", "osc", }; +static const char *perclk_sels[] = { "ipg", "osc", }; +static const char *lcdif_sels[] = { "lcdif_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *csi_sels[] = { "osc", "pll2_pfd2_396m", "pll3_120m", "pll3_pfd1_540m", }; +static const char *sim_sels[] = { "sim_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +/* epdc_pre_sels, epdc_sels, esai_sels only exists on i.MX6ULL */ +static const char *epdc_pre_sels[] = { "pll2_bus", "pll3_usb_otg", "pll5_video_div", "pll2_pfd0_352m", "pll2_pfd2_396m", "pll3_pfd2_508m", }; +static const char *esai_sels[] = { "pll4_audio_div", "pll3_pfd2_508m", "pll5_video_div", "pll3_usb_otg", }; +static const char *epdc_sels[] = { "epdc_podf", "ipp_di0", "ipp_di1", "ldb_di0", "ldb_di1", }; +static const char *cko1_sels[] = { "dummy", "dummy", "dummy", "dummy", "dummy", "axi", "enfc", "dummy", "dummy", + "dummy", "lcdif_pix", "ahb", "ipg", "ipg_per", "ckil", "pll4_audio_div", }; +static const char *cko2_sels[] = { "dummy", "dummy", "dummy", "usdhc1", "dummy", "dummy", "ecspi_root", "dummy", + "dummy", "dummy", "dummy", "dummy", "dummy", "dummy", "osc", "dummy", + "dummy", "usdhc2", "sai1", "sai2", "sai3", "dummy", "dummy", "can_root", + "dummy", "dummy", "dummy", "dummy", "uart_serial", "spdif", "dummy", "dummy", }; +static const char *cko_sels[] = { "cko1", "cko2", }; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; + +static const struct clk_div_table clk_enet_ref_table[] = { + { .val = 0, .div = 20, }, + { .val = 1, .div = 10, }, + { .val = 2, .div = 5, }, + { .val = 3, .div = 4, }, + { } +}; + +static const struct clk_div_table post_div_table[] = { + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static const struct clk_div_table video_div_table[] = { + { .val = 0, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 2, .div = 1, }, + { .val = 3, .div = 4, }, + { } +}; + +static u32 share_count_asrc; +static u32 share_count_audio; +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_esai; + +static inline int clk_on_imx6ul(void) +{ + return of_machine_is_compatible("fsl,imx6ul"); +} + +static inline int clk_on_imx6ull(void) +{ + return of_machine_is_compatible("fsl,imx6ull"); +} + +static void __init imx6ul_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + IMX6UL_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return; + + clk_hw_data->num = IMX6UL_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX6UL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + hws[IMX6UL_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); + hws[IMX6UL_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); + + /* ipp_di clock is external input */ + hws[IMX6UL_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0"); + hws[IMX6UL_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-anatop"); + base = of_iomap(np, 0); + of_node_put(np); + WARN_ON(!base); + + hws[IMX6UL_PLL1_BYPASS_SRC] = imx_clk_hw_mux("pll1_bypass_src", base + 0x00, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6UL_PLL2_BYPASS_SRC] = imx_clk_hw_mux("pll2_bypass_src", base + 0x30, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6UL_PLL3_BYPASS_SRC] = imx_clk_hw_mux("pll3_bypass_src", base + 0x10, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6UL_PLL4_BYPASS_SRC] = imx_clk_hw_mux("pll4_bypass_src", base + 0x70, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6UL_PLL5_BYPASS_SRC] = imx_clk_hw_mux("pll5_bypass_src", base + 0xa0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6UL_PLL6_BYPASS_SRC] = imx_clk_hw_mux("pll6_bypass_src", base + 0xe0, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + hws[IMX6UL_PLL7_BYPASS_SRC] = imx_clk_hw_mux("pll7_bypass_src", base + 0x20, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + + hws[IMX6UL_CLK_PLL1] = imx_clk_hw_pllv3(IMX_PLLV3_SYS, "pll1", "osc", base + 0x00, 0x7f); + hws[IMX6UL_CLK_PLL2] = imx_clk_hw_pllv3(IMX_PLLV3_GENERIC, "pll2", "osc", base + 0x30, 0x1); + hws[IMX6UL_CLK_PLL3] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll3", "osc", base + 0x10, 0x3); + hws[IMX6UL_CLK_PLL4] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll4", "osc", base + 0x70, 0x7f); + hws[IMX6UL_CLK_PLL5] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll5", "osc", base + 0xa0, 0x7f); + hws[IMX6UL_CLK_PLL6] = imx_clk_hw_pllv3(IMX_PLLV3_ENET, "pll6", "osc", base + 0xe0, 0x3); + hws[IMX6UL_CLK_PLL7] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll7", "osc", base + 0x20, 0x3); + + hws[IMX6UL_PLL1_BYPASS] = imx_clk_hw_mux_flags("pll1_bypass", base + 0x00, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6UL_PLL2_BYPASS] = imx_clk_hw_mux_flags("pll2_bypass", base + 0x30, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6UL_PLL3_BYPASS] = imx_clk_hw_mux_flags("pll3_bypass", base + 0x10, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6UL_PLL4_BYPASS] = imx_clk_hw_mux_flags("pll4_bypass", base + 0x70, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6UL_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6UL_PLL6_BYPASS] = imx_clk_hw_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX6UL_PLL7_BYPASS] = imx_clk_hw_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); + + /* Do not bypass PLLs initially */ + clk_set_parent(hws[IMX6UL_PLL1_BYPASS]->clk, hws[IMX6UL_CLK_PLL1]->clk); + clk_set_parent(hws[IMX6UL_PLL2_BYPASS]->clk, hws[IMX6UL_CLK_PLL2]->clk); + clk_set_parent(hws[IMX6UL_PLL3_BYPASS]->clk, hws[IMX6UL_CLK_PLL3]->clk); + clk_set_parent(hws[IMX6UL_PLL4_BYPASS]->clk, hws[IMX6UL_CLK_PLL4]->clk); + clk_set_parent(hws[IMX6UL_PLL5_BYPASS]->clk, hws[IMX6UL_CLK_PLL5]->clk); + clk_set_parent(hws[IMX6UL_PLL6_BYPASS]->clk, hws[IMX6UL_CLK_PLL6]->clk); + clk_set_parent(hws[IMX6UL_PLL7_BYPASS]->clk, hws[IMX6UL_CLK_PLL7]->clk); + + hws[IMX6UL_CLK_PLL1_SYS] = imx_clk_hw_fixed_factor("pll1_sys", "pll1_bypass", 1, 1); + hws[IMX6UL_CLK_PLL2_BUS] = imx_clk_hw_gate("pll2_bus", "pll2_bypass", base + 0x30, 13); + hws[IMX6UL_CLK_PLL3_USB_OTG] = imx_clk_hw_gate("pll3_usb_otg", "pll3_bypass", base + 0x10, 13); + hws[IMX6UL_CLK_PLL4_AUDIO] = imx_clk_hw_gate("pll4_audio", "pll4_bypass", base + 0x70, 13); + hws[IMX6UL_CLK_PLL5_VIDEO] = imx_clk_hw_gate("pll5_video", "pll5_bypass", base + 0xa0, 13); + hws[IMX6UL_CLK_PLL6_ENET] = imx_clk_hw_gate("pll6_enet", "pll6_bypass", base + 0xe0, 13); + hws[IMX6UL_CLK_PLL7_USB_HOST] = imx_clk_hw_gate("pll7_usb_host", "pll7_bypass", base + 0x20, 13); + + /* + * Bit 20 is the reserved and read-only bit, we do this only for: + * - Do nothing for usbphy clk_enable/disable + * - Keep refcount when do usbphy clk_enable/disable, in that case, + * the clk framework many need to enable/disable usbphy's parent + */ + hws[IMX6UL_CLK_USBPHY1] = imx_clk_hw_gate("usbphy1", "pll3_usb_otg", base + 0x10, 20); + hws[IMX6UL_CLK_USBPHY2] = imx_clk_hw_gate("usbphy2", "pll7_usb_host", base + 0x20, 20); + + /* + * usbphy*_gate needs to be on after system boots up, and software + * never needs to control it anymore. + */ + hws[IMX6UL_CLK_USBPHY1_GATE] = imx_clk_hw_gate("usbphy1_gate", "dummy", base + 0x10, 6); + hws[IMX6UL_CLK_USBPHY2_GATE] = imx_clk_hw_gate("usbphy2_gate", "dummy", base + 0x20, 6); + + /* name parent_name reg idx */ + hws[IMX6UL_CLK_PLL2_PFD0] = imx_clk_hw_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0); + hws[IMX6UL_CLK_PLL2_PFD1] = imx_clk_hw_pfd("pll2_pfd1_594m", "pll2_bus", base + 0x100, 1); + hws[IMX6UL_CLK_PLL2_PFD2] = imx_clk_hw_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2); + hws[IMX6UL_CLK_PLL2_PFD3] = imx_clk_hw_pfd("pll2_pfd3_594m", "pll2_bus", base + 0x100, 3); + hws[IMX6UL_CLK_PLL3_PFD0] = imx_clk_hw_pfd("pll3_pfd0_720m", "pll3_usb_otg", base + 0xf0, 0); + hws[IMX6UL_CLK_PLL3_PFD1] = imx_clk_hw_pfd("pll3_pfd1_540m", "pll3_usb_otg", base + 0xf0, 1); + hws[IMX6UL_CLK_PLL3_PFD2] = imx_clk_hw_pfd("pll3_pfd2_508m", "pll3_usb_otg", base + 0xf0, 2); + hws[IMX6UL_CLK_PLL3_PFD3] = imx_clk_hw_pfd("pll3_pfd3_454m", "pll3_usb_otg", base + 0xf0, 3); + + hws[IMX6UL_CLK_ENET_REF] = clk_hw_register_divider_table(NULL, "enet_ref", "pll6_enet", 0, + base + 0xe0, 0, 2, 0, clk_enet_ref_table, &imx_ccm_lock); + hws[IMX6UL_CLK_ENET2_REF] = clk_hw_register_divider_table(NULL, "enet2_ref", "pll6_enet", 0, + base + 0xe0, 2, 2, 0, clk_enet_ref_table, &imx_ccm_lock); + + hws[IMX6UL_CLK_ENET2_REF_125M] = imx_clk_hw_gate("enet_ref_125m", "enet2_ref", base + 0xe0, 20); + hws[IMX6UL_CLK_ENET_PTP_REF] = imx_clk_hw_fixed_factor("enet_ptp_ref", "pll6_enet", 1, 20); + hws[IMX6UL_CLK_ENET_PTP] = imx_clk_hw_gate("enet_ptp", "enet_ptp_ref", base + 0xe0, 21); + + hws[IMX6UL_CLK_PLL4_POST_DIV] = clk_hw_register_divider_table(NULL, "pll4_post_div", "pll4_audio", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6UL_CLK_PLL4_AUDIO_DIV] = clk_hw_register_divider(NULL, "pll4_audio_div", "pll4_post_div", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 15, 1, 0, &imx_ccm_lock); + hws[IMX6UL_CLK_PLL5_POST_DIV] = clk_hw_register_divider_table(NULL, "pll5_post_div", "pll5_video", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xa0, 19, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX6UL_CLK_PLL5_VIDEO_DIV] = clk_hw_register_divider_table(NULL, "pll5_video_div", "pll5_post_div", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x170, 30, 2, 0, video_div_table, &imx_ccm_lock); + + /* name parent_name mult div */ + hws[IMX6UL_CLK_PLL2_198M] = imx_clk_hw_fixed_factor("pll2_198m", "pll2_pfd2_396m", 1, 2); + hws[IMX6UL_CLK_PLL3_80M] = imx_clk_hw_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + hws[IMX6UL_CLK_PLL3_60M] = imx_clk_hw_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8); + hws[IMX6UL_CLK_GPT_3M] = imx_clk_hw_fixed_factor("gpt_3m", "osc", 1, 8); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + + hws[IMX6UL_CA7_SECONDARY_SEL] = imx_clk_hw_mux("ca7_secondary_sel", base + 0xc, 3, 1, ca7_secondary_sels, ARRAY_SIZE(ca7_secondary_sels)); + hws[IMX6UL_CLK_STEP] = imx_clk_hw_mux("step", base + 0x0c, 8, 1, step_sels, ARRAY_SIZE(step_sels)); + hws[IMX6UL_CLK_PLL1_SW] = imx_clk_hw_mux_flags("pll1_sw", base + 0x0c, 2, 1, pll1_sw_sels, ARRAY_SIZE(pll1_sw_sels), 0); + hws[IMX6UL_CLK_AXI_ALT_SEL] = imx_clk_hw_mux("axi_alt_sel", base + 0x14, 7, 1, axi_alt_sels, ARRAY_SIZE(axi_alt_sels)); + hws[IMX6UL_CLK_AXI_SEL] = imx_clk_hw_mux_flags("axi_sel", base + 0x14, 6, 1, axi_sels, ARRAY_SIZE(axi_sels), 0); + hws[IMX6UL_CLK_PERIPH_PRE] = imx_clk_hw_mux("periph_pre", base + 0x18, 18, 2, periph_pre_sels, ARRAY_SIZE(periph_pre_sels)); + hws[IMX6UL_CLK_PERIPH2_PRE] = imx_clk_hw_mux("periph2_pre", base + 0x18, 21, 2, periph2_pre_sels, ARRAY_SIZE(periph2_pre_sels)); + hws[IMX6UL_CLK_PERIPH_CLK2_SEL] = imx_clk_hw_mux("periph_clk2_sel", base + 0x18, 12, 2, periph_clk2_sels, ARRAY_SIZE(periph_clk2_sels)); + hws[IMX6UL_CLK_PERIPH2_CLK2_SEL] = imx_clk_hw_mux("periph2_clk2_sel", base + 0x18, 20, 1, periph2_clk2_sels, ARRAY_SIZE(periph2_clk2_sels)); + hws[IMX6UL_CLK_EIM_SLOW_SEL] = imx_clk_hw_mux("eim_slow_sel", base + 0x1c, 29, 2, eim_slow_sels, ARRAY_SIZE(eim_slow_sels)); + hws[IMX6UL_CLK_GPMI_SEL] = imx_clk_hw_mux("gpmi_sel", base + 0x1c, 19, 1, gpmi_sels, ARRAY_SIZE(gpmi_sels)); + hws[IMX6UL_CLK_BCH_SEL] = imx_clk_hw_mux("bch_sel", base + 0x1c, 18, 1, bch_sels, ARRAY_SIZE(bch_sels)); + hws[IMX6UL_CLK_USDHC2_SEL] = imx_clk_hw_mux("usdhc2_sel", base + 0x1c, 17, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6UL_CLK_USDHC1_SEL] = imx_clk_hw_mux("usdhc1_sel", base + 0x1c, 16, 1, usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMX6UL_CLK_SAI3_SEL] = imx_clk_hw_mux("sai3_sel", base + 0x1c, 14, 2, sai_sels, ARRAY_SIZE(sai_sels)); + hws[IMX6UL_CLK_SAI2_SEL] = imx_clk_hw_mux("sai2_sel", base + 0x1c, 12, 2, sai_sels, ARRAY_SIZE(sai_sels)); + hws[IMX6UL_CLK_SAI1_SEL] = imx_clk_hw_mux("sai1_sel", base + 0x1c, 10, 2, sai_sels, ARRAY_SIZE(sai_sels)); + hws[IMX6UL_CLK_QSPI1_SEL] = imx_clk_hw_mux("qspi1_sel", base + 0x1c, 7, 3, qspi1_sels, ARRAY_SIZE(qspi1_sels)); + hws[IMX6UL_CLK_PERCLK_SEL] = imx_clk_hw_mux("perclk_sel", base + 0x1c, 6, 1, perclk_sels, ARRAY_SIZE(perclk_sels)); + hws[IMX6UL_CLK_CAN_SEL] = imx_clk_hw_mux("can_sel", base + 0x20, 8, 2, can_sels, ARRAY_SIZE(can_sels)); + if (clk_on_imx6ull()) + hws[IMX6ULL_CLK_ESAI_SEL] = imx_clk_hw_mux("esai_sel", base + 0x20, 19, 2, esai_sels, ARRAY_SIZE(esai_sels)); + hws[IMX6UL_CLK_UART_SEL] = imx_clk_hw_mux("uart_sel", base + 0x24, 6, 1, uart_sels, ARRAY_SIZE(uart_sels)); + hws[IMX6UL_CLK_ENFC_SEL] = imx_clk_hw_mux("enfc_sel", base + 0x2c, 15, 3, enfc_sels, ARRAY_SIZE(enfc_sels)); + hws[IMX6UL_CLK_LDB_DI0_SEL] = imx_clk_hw_mux("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di0_sels, ARRAY_SIZE(ldb_di0_sels)); + hws[IMX6UL_CLK_SPDIF_SEL] = imx_clk_hw_mux("spdif_sel", base + 0x30, 20, 2, spdif_sels, ARRAY_SIZE(spdif_sels)); + if (clk_on_imx6ul()) { + hws[IMX6UL_CLK_SIM_PRE_SEL] = imx_clk_hw_mux("sim_pre_sel", base + 0x34, 15, 3, sim_pre_sels, ARRAY_SIZE(sim_pre_sels)); + hws[IMX6UL_CLK_SIM_SEL] = imx_clk_hw_mux("sim_sel", base + 0x34, 9, 3, sim_sels, ARRAY_SIZE(sim_sels)); + } else if (clk_on_imx6ull()) { + hws[IMX6ULL_CLK_EPDC_PRE_SEL] = imx_clk_hw_mux("epdc_pre_sel", base + 0x34, 15, 3, epdc_pre_sels, ARRAY_SIZE(epdc_pre_sels)); + hws[IMX6ULL_CLK_EPDC_SEL] = imx_clk_hw_mux("epdc_sel", base + 0x34, 9, 3, epdc_sels, ARRAY_SIZE(epdc_sels)); + } + hws[IMX6UL_CLK_ECSPI_SEL] = imx_clk_hw_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels)); + hws[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_hw_mux_flags("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels), CLK_SET_RATE_PARENT); + hws[IMX6UL_CLK_LCDIF_SEL] = imx_clk_hw_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels)); + hws[IMX6UL_CLK_CSI_SEL] = imx_clk_hw_mux("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels)); + + hws[IMX6UL_CLK_LDB_DI0_DIV_SEL] = imx_clk_hw_mux("ldb_di0", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels)); + hws[IMX6UL_CLK_LDB_DI1_DIV_SEL] = imx_clk_hw_mux("ldb_di1", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels)); + + hws[IMX6UL_CLK_CKO1_SEL] = imx_clk_hw_mux("cko1_sel", base + 0x60, 0, 4, cko1_sels, ARRAY_SIZE(cko1_sels)); + hws[IMX6UL_CLK_CKO2_SEL] = imx_clk_hw_mux("cko2_sel", base + 0x60, 16, 5, cko2_sels, ARRAY_SIZE(cko2_sels)); + hws[IMX6UL_CLK_CKO] = imx_clk_hw_mux("cko", base + 0x60, 8, 1, cko_sels, ARRAY_SIZE(cko_sels)); + + hws[IMX6UL_CLK_LDB_DI0_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di0_div_3_5", "ldb_di0_sel", 2, 7); + hws[IMX6UL_CLK_LDB_DI0_DIV_7] = imx_clk_hw_fixed_factor("ldb_di0_div_7", "ldb_di0_sel", 1, 7); + hws[IMX6UL_CLK_LDB_DI1_DIV_3_5] = imx_clk_hw_fixed_factor("ldb_di1_div_3_5", "qspi1_sel", 2, 7); + hws[IMX6UL_CLK_LDB_DI1_DIV_7] = imx_clk_hw_fixed_factor("ldb_di1_div_7", "qspi1_sel", 1, 7); + + hws[IMX6UL_CLK_PERIPH] = imx_clk_hw_busy_mux("periph", base + 0x14, 25, 1, base + 0x48, 5, periph_sels, ARRAY_SIZE(periph_sels)); + hws[IMX6UL_CLK_PERIPH2] = imx_clk_hw_busy_mux("periph2", base + 0x14, 26, 1, base + 0x48, 3, periph2_sels, ARRAY_SIZE(periph2_sels)); + + hws[IMX6UL_CLK_PERIPH_CLK2] = imx_clk_hw_divider("periph_clk2", "periph_clk2_sel", base + 0x14, 27, 3); + hws[IMX6UL_CLK_PERIPH2_CLK2] = imx_clk_hw_divider("periph2_clk2", "periph2_clk2_sel", base + 0x14, 0, 3); + hws[IMX6UL_CLK_IPG] = imx_clk_hw_divider("ipg", "ahb", base + 0x14, 8, 2); + hws[IMX6UL_CLK_LCDIF_PODF] = imx_clk_hw_divider("lcdif_podf", "lcdif_pred", base + 0x18, 23, 3); + hws[IMX6UL_CLK_QSPI1_PDOF] = imx_clk_hw_divider("qspi1_podf", "qspi1_sel", base + 0x1c, 26, 3); + hws[IMX6UL_CLK_EIM_SLOW_PODF] = imx_clk_hw_divider("eim_slow_podf", "eim_slow_sel", base + 0x1c, 23, 3); + hws[IMX6UL_CLK_PERCLK] = imx_clk_hw_divider("perclk", "perclk_sel", base + 0x1c, 0, 6); + hws[IMX6UL_CLK_CAN_PODF] = imx_clk_hw_divider("can_podf", "can_sel", base + 0x20, 2, 6); + hws[IMX6UL_CLK_GPMI_PODF] = imx_clk_hw_divider("gpmi_podf", "gpmi_sel", base + 0x24, 22, 3); + hws[IMX6UL_CLK_BCH_PODF] = imx_clk_hw_divider("bch_podf", "bch_sel", base + 0x24, 19, 3); + hws[IMX6UL_CLK_USDHC2_PODF] = imx_clk_hw_divider("usdhc2_podf", "usdhc2_sel", base + 0x24, 16, 3); + hws[IMX6UL_CLK_USDHC1_PODF] = imx_clk_hw_divider("usdhc1_podf", "usdhc1_sel", base + 0x24, 11, 3); + hws[IMX6UL_CLK_UART_PODF] = imx_clk_hw_divider("uart_podf", "uart_sel", base + 0x24, 0, 6); + hws[IMX6UL_CLK_SAI3_PRED] = imx_clk_hw_divider("sai3_pred", "sai3_sel", base + 0x28, 22, 3); + hws[IMX6UL_CLK_SAI3_PODF] = imx_clk_hw_divider("sai3_podf", "sai3_pred", base + 0x28, 16, 6); + hws[IMX6UL_CLK_SAI1_PRED] = imx_clk_hw_divider("sai1_pred", "sai1_sel", base + 0x28, 6, 3); + hws[IMX6UL_CLK_SAI1_PODF] = imx_clk_hw_divider("sai1_podf", "sai1_pred", base + 0x28, 0, 6); + if (clk_on_imx6ull()) { + hws[IMX6ULL_CLK_ESAI_PRED] = imx_clk_hw_divider("esai_pred", "esai_sel", base + 0x28, 9, 3); + hws[IMX6ULL_CLK_ESAI_PODF] = imx_clk_hw_divider("esai_podf", "esai_pred", base + 0x28, 25, 3); + } + hws[IMX6UL_CLK_ENFC_PRED] = imx_clk_hw_divider("enfc_pred", "enfc_sel", base + 0x2c, 18, 3); + hws[IMX6UL_CLK_ENFC_PODF] = imx_clk_hw_divider("enfc_podf", "enfc_pred", base + 0x2c, 21, 6); + hws[IMX6UL_CLK_SAI2_PRED] = imx_clk_hw_divider("sai2_pred", "sai2_sel", base + 0x2c, 6, 3); + hws[IMX6UL_CLK_SAI2_PODF] = imx_clk_hw_divider("sai2_podf", "sai2_pred", base + 0x2c, 0, 6); + hws[IMX6UL_CLK_SPDIF_PRED] = imx_clk_hw_divider("spdif_pred", "spdif_sel", base + 0x30, 25, 3); + hws[IMX6UL_CLK_SPDIF_PODF] = imx_clk_hw_divider("spdif_podf", "spdif_pred", base + 0x30, 22, 3); + if (clk_on_imx6ul()) + hws[IMX6UL_CLK_SIM_PODF] = imx_clk_hw_divider("sim_podf", "sim_pre_sel", base + 0x34, 12, 3); + else if (clk_on_imx6ull()) + hws[IMX6ULL_CLK_EPDC_PODF] = imx_clk_hw_divider("epdc_podf", "epdc_pre_sel", base + 0x34, 12, 3); + hws[IMX6UL_CLK_ECSPI_PODF] = imx_clk_hw_divider("ecspi_podf", "ecspi_sel", base + 0x38, 19, 6); + hws[IMX6UL_CLK_LCDIF_PRED] = imx_clk_hw_divider("lcdif_pred", "lcdif_pre_sel", base + 0x38, 12, 3); + hws[IMX6UL_CLK_CSI_PODF] = imx_clk_hw_divider("csi_podf", "csi_sel", base + 0x3c, 11, 3); + + hws[IMX6UL_CLK_CKO1_PODF] = imx_clk_hw_divider("cko1_podf", "cko1_sel", base + 0x60, 4, 3); + hws[IMX6UL_CLK_CKO2_PODF] = imx_clk_hw_divider("cko2_podf", "cko2_sel", base + 0x60, 21, 3); + + hws[IMX6UL_CLK_ARM] = imx_clk_hw_busy_divider("arm", "pll1_sw", base + 0x10, 0, 3, base + 0x48, 16); + hws[IMX6UL_CLK_MMDC_PODF] = imx_clk_hw_busy_divider("mmdc_podf", "periph2", base + 0x14, 3, 3, base + 0x48, 2); + hws[IMX6UL_CLK_AXI_PODF] = imx_clk_hw_busy_divider("axi_podf", "axi_sel", base + 0x14, 16, 3, base + 0x48, 0); + hws[IMX6UL_CLK_AHB] = imx_clk_hw_busy_divider("ahb", "periph", base + 0x14, 10, 3, base + 0x48, 1); + + /* CCGR0 */ + hws[IMX6UL_CLK_AIPSTZ1] = imx_clk_hw_gate2_flags("aips_tz1", "ahb", base + 0x68, 0, CLK_IS_CRITICAL); + hws[IMX6UL_CLK_AIPSTZ2] = imx_clk_hw_gate2_flags("aips_tz2", "ahb", base + 0x68, 2, CLK_IS_CRITICAL); + hws[IMX6UL_CLK_APBHDMA] = imx_clk_hw_gate2("apbh_dma", "bch_podf", base + 0x68, 4); + hws[IMX6UL_CLK_ASRC_IPG] = imx_clk_hw_gate2_shared("asrc_ipg", "ahb", base + 0x68, 6, &share_count_asrc); + hws[IMX6UL_CLK_ASRC_MEM] = imx_clk_hw_gate2_shared("asrc_mem", "ahb", base + 0x68, 6, &share_count_asrc); + if (clk_on_imx6ul()) { + hws[IMX6UL_CLK_CAAM_MEM] = imx_clk_hw_gate2("caam_mem", "ahb", base + 0x68, 8); + hws[IMX6UL_CLK_CAAM_ACLK] = imx_clk_hw_gate2("caam_aclk", "ahb", base + 0x68, 10); + hws[IMX6UL_CLK_CAAM_IPG] = imx_clk_hw_gate2("caam_ipg", "ipg", base + 0x68, 12); + } else if (clk_on_imx6ull()) { + hws[IMX6ULL_CLK_DCP_CLK] = imx_clk_hw_gate2("dcp", "ahb", base + 0x68, 10); + hws[IMX6UL_CLK_ENET] = imx_clk_hw_gate2("enet", "ipg", base + 0x68, 12); + hws[IMX6UL_CLK_ENET_AHB] = imx_clk_hw_gate2("enet_ahb", "ahb", base + 0x68, 12); + } + hws[IMX6UL_CLK_CAN1_IPG] = imx_clk_hw_gate2("can1_ipg", "ipg", base + 0x68, 14); + hws[IMX6UL_CLK_CAN1_SERIAL] = imx_clk_hw_gate2("can1_serial", "can_podf", base + 0x68, 16); + hws[IMX6UL_CLK_CAN2_IPG] = imx_clk_hw_gate2("can2_ipg", "ipg", base + 0x68, 18); + hws[IMX6UL_CLK_CAN2_SERIAL] = imx_clk_hw_gate2("can2_serial", "can_podf", base + 0x68, 20); + hws[IMX6UL_CLK_GPT2_BUS] = imx_clk_hw_gate2("gpt2_bus", "perclk", base + 0x68, 24); + hws[IMX6UL_CLK_GPT2_SERIAL] = imx_clk_hw_gate2("gpt2_serial", "perclk", base + 0x68, 26); + hws[IMX6UL_CLK_UART2_IPG] = imx_clk_hw_gate2("uart2_ipg", "ipg", base + 0x68, 28); + hws[IMX6UL_CLK_UART2_SERIAL] = imx_clk_hw_gate2("uart2_serial", "uart_podf", base + 0x68, 28); + if (clk_on_imx6ull()) + hws[IMX6UL_CLK_AIPSTZ3] = imx_clk_hw_gate2("aips_tz3", "ahb", base + 0x80, 18); + hws[IMX6UL_CLK_GPIO2] = imx_clk_hw_gate2("gpio2", "ipg", base + 0x68, 30); + + /* CCGR1 */ + hws[IMX6UL_CLK_ECSPI1] = imx_clk_hw_gate2("ecspi1", "ecspi_podf", base + 0x6c, 0); + hws[IMX6UL_CLK_ECSPI2] = imx_clk_hw_gate2("ecspi2", "ecspi_podf", base + 0x6c, 2); + hws[IMX6UL_CLK_ECSPI3] = imx_clk_hw_gate2("ecspi3", "ecspi_podf", base + 0x6c, 4); + hws[IMX6UL_CLK_ECSPI4] = imx_clk_hw_gate2("ecspi4", "ecspi_podf", base + 0x6c, 6); + hws[IMX6UL_CLK_ADC2] = imx_clk_hw_gate2("adc2", "ipg", base + 0x6c, 8); + hws[IMX6UL_CLK_UART3_IPG] = imx_clk_hw_gate2("uart3_ipg", "ipg", base + 0x6c, 10); + hws[IMX6UL_CLK_UART3_SERIAL] = imx_clk_hw_gate2("uart3_serial", "uart_podf", base + 0x6c, 10); + hws[IMX6UL_CLK_EPIT1] = imx_clk_hw_gate2("epit1", "perclk", base + 0x6c, 12); + hws[IMX6UL_CLK_EPIT2] = imx_clk_hw_gate2("epit2", "perclk", base + 0x6c, 14); + hws[IMX6UL_CLK_ADC1] = imx_clk_hw_gate2("adc1", "ipg", base + 0x6c, 16); + hws[IMX6UL_CLK_GPT1_BUS] = imx_clk_hw_gate2("gpt1_bus", "perclk", base + 0x6c, 20); + hws[IMX6UL_CLK_GPT1_SERIAL] = imx_clk_hw_gate2("gpt1_serial", "perclk", base + 0x6c, 22); + hws[IMX6UL_CLK_UART4_IPG] = imx_clk_hw_gate2("uart4_ipg", "ipg", base + 0x6c, 24); + hws[IMX6UL_CLK_UART4_SERIAL] = imx_clk_hw_gate2("uart4_serial", "uart_podf", base + 0x6c, 24); + hws[IMX6UL_CLK_GPIO1] = imx_clk_hw_gate2("gpio1", "ipg", base + 0x6c, 26); + hws[IMX6UL_CLK_GPIO5] = imx_clk_hw_gate2("gpio5", "ipg", base + 0x6c, 30); + + /* CCGR2 */ + if (clk_on_imx6ull()) { + hws[IMX6ULL_CLK_ESAI_EXTAL] = imx_clk_hw_gate2_shared("esai_extal", "esai_podf", base + 0x70, 0, &share_count_esai); + hws[IMX6ULL_CLK_ESAI_IPG] = imx_clk_hw_gate2_shared("esai_ipg", "ahb", base + 0x70, 0, &share_count_esai); + hws[IMX6ULL_CLK_ESAI_MEM] = imx_clk_hw_gate2_shared("esai_mem", "ahb", base + 0x70, 0, &share_count_esai); + } + hws[IMX6UL_CLK_I2C1] = imx_clk_hw_gate2("i2c1", "perclk", base + 0x70, 6); + hws[IMX6UL_CLK_I2C2] = imx_clk_hw_gate2("i2c2", "perclk", base + 0x70, 8); + hws[IMX6UL_CLK_I2C3] = imx_clk_hw_gate2("i2c3", "perclk", base + 0x70, 10); + hws[IMX6UL_CLK_OCOTP] = imx_clk_hw_gate2("ocotp", "ipg", base + 0x70, 12); + hws[IMX6UL_CLK_IOMUXC] = imx_clk_hw_gate2("iomuxc", "lcdif_podf", base + 0x70, 14); + hws[IMX6UL_CLK_GPIO3] = imx_clk_hw_gate2("gpio3", "ipg", base + 0x70, 26); + hws[IMX6UL_CLK_LCDIF_APB] = imx_clk_hw_gate2("lcdif_apb", "axi", base + 0x70, 28); + hws[IMX6UL_CLK_PXP] = imx_clk_hw_gate2("pxp", "axi", base + 0x70, 30); + + /* CCGR3 */ + /* + * Although the imx6ull reference manual lists CCGR2 as the csi clk + * gate register, tests have shown that it is actually the CCGR3 + * register bit 0/1, same as for the imx6ul. + */ + hws[IMX6UL_CLK_CSI] = imx_clk_hw_gate2("csi", "csi_podf", base + 0x74, 0); + hws[IMX6UL_CLK_UART5_IPG] = imx_clk_hw_gate2("uart5_ipg", "ipg", base + 0x74, 2); + hws[IMX6UL_CLK_UART5_SERIAL] = imx_clk_hw_gate2("uart5_serial", "uart_podf", base + 0x74, 2); + if (clk_on_imx6ul()) { + hws[IMX6UL_CLK_ENET] = imx_clk_hw_gate2("enet", "ipg", base + 0x74, 4); + hws[IMX6UL_CLK_ENET_AHB] = imx_clk_hw_gate2("enet_ahb", "ahb", base + 0x74, 4); + } else if (clk_on_imx6ull()) { + hws[IMX6ULL_CLK_EPDC_ACLK] = imx_clk_hw_gate2("epdc_aclk", "axi", base + 0x74, 4); + hws[IMX6ULL_CLK_EPDC_PIX] = imx_clk_hw_gate2("epdc_pix", "epdc_podf", base + 0x74, 4); + } + hws[IMX6UL_CLK_UART6_IPG] = imx_clk_hw_gate2("uart6_ipg", "ipg", base + 0x74, 6); + hws[IMX6UL_CLK_UART6_SERIAL] = imx_clk_hw_gate2("uart6_serial", "uart_podf", base + 0x74, 6); + hws[IMX6UL_CLK_LCDIF_PIX] = imx_clk_hw_gate2("lcdif_pix", "lcdif_podf", base + 0x74, 10); + hws[IMX6UL_CLK_GPIO4] = imx_clk_hw_gate2("gpio4", "ipg", base + 0x74, 12); + hws[IMX6UL_CLK_QSPI] = imx_clk_hw_gate2("qspi1", "qspi1_podf", base + 0x74, 14); + hws[IMX6UL_CLK_WDOG1] = imx_clk_hw_gate2("wdog1", "ipg", base + 0x74, 16); + hws[IMX6UL_CLK_MMDC_P0_FAST] = imx_clk_hw_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); + hws[IMX6UL_CLK_MMDC_P0_IPG] = imx_clk_hw_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + hws[IMX6UL_CLK_MMDC_P1_IPG] = imx_clk_hw_gate2_flags("mmdc_p1_ipg", "ipg", base + 0x74, 26, CLK_IS_CRITICAL); + hws[IMX6UL_CLK_AXI] = imx_clk_hw_gate_flags("axi", "axi_podf", base + 0x74, 28, CLK_IS_CRITICAL); + + /* CCGR4 */ + hws[IMX6UL_CLK_PER_BCH] = imx_clk_hw_gate2("per_bch", "bch_podf", base + 0x78, 12); + hws[IMX6UL_CLK_PWM1] = imx_clk_hw_gate2("pwm1", "perclk", base + 0x78, 16); + hws[IMX6UL_CLK_PWM2] = imx_clk_hw_gate2("pwm2", "perclk", base + 0x78, 18); + hws[IMX6UL_CLK_PWM3] = imx_clk_hw_gate2("pwm3", "perclk", base + 0x78, 20); + hws[IMX6UL_CLK_PWM4] = imx_clk_hw_gate2("pwm4", "perclk", base + 0x78, 22); + hws[IMX6UL_CLK_GPMI_BCH_APB] = imx_clk_hw_gate2("gpmi_bch_apb", "bch_podf", base + 0x78, 24); + hws[IMX6UL_CLK_GPMI_BCH] = imx_clk_hw_gate2("gpmi_bch", "gpmi_podf", base + 0x78, 26); + hws[IMX6UL_CLK_GPMI_IO] = imx_clk_hw_gate2("gpmi_io", "enfc_podf", base + 0x78, 28); + hws[IMX6UL_CLK_GPMI_APB] = imx_clk_hw_gate2("gpmi_apb", "bch_podf", base + 0x78, 30); + + /* CCGR5 */ + hws[IMX6UL_CLK_ROM] = imx_clk_hw_gate2_flags("rom", "ahb", base + 0x7c, 0, CLK_IS_CRITICAL); + hws[IMX6UL_CLK_SDMA] = imx_clk_hw_gate2("sdma", "ahb", base + 0x7c, 6); + hws[IMX6UL_CLK_KPP] = imx_clk_hw_gate2("kpp", "ipg", base + 0x7c, 8); + hws[IMX6UL_CLK_WDOG2] = imx_clk_hw_gate2("wdog2", "ipg", base + 0x7c, 10); + hws[IMX6UL_CLK_SPBA] = imx_clk_hw_gate2("spba", "ipg", base + 0x7c, 12); + hws[IMX6UL_CLK_SPDIF] = imx_clk_hw_gate2_shared("spdif", "spdif_podf", base + 0x7c, 14, &share_count_audio); + hws[IMX6UL_CLK_SPDIF_GCLK] = imx_clk_hw_gate2_shared("spdif_gclk", "ipg", base + 0x7c, 14, &share_count_audio); + hws[IMX6UL_CLK_SAI3] = imx_clk_hw_gate2_shared("sai3", "sai3_podf", base + 0x7c, 22, &share_count_sai3); + hws[IMX6UL_CLK_SAI3_IPG] = imx_clk_hw_gate2_shared("sai3_ipg", "ipg", base + 0x7c, 22, &share_count_sai3); + hws[IMX6UL_CLK_UART1_IPG] = imx_clk_hw_gate2("uart1_ipg", "ipg", base + 0x7c, 24); + hws[IMX6UL_CLK_UART1_SERIAL] = imx_clk_hw_gate2("uart1_serial", "uart_podf", base + 0x7c, 24); + hws[IMX6UL_CLK_UART7_IPG] = imx_clk_hw_gate2("uart7_ipg", "ipg", base + 0x7c, 26); + hws[IMX6UL_CLK_UART7_SERIAL] = imx_clk_hw_gate2("uart7_serial", "uart_podf", base + 0x7c, 26); + hws[IMX6UL_CLK_SAI1] = imx_clk_hw_gate2_shared("sai1", "sai1_podf", base + 0x7c, 28, &share_count_sai1); + hws[IMX6UL_CLK_SAI1_IPG] = imx_clk_hw_gate2_shared("sai1_ipg", "ipg", base + 0x7c, 28, &share_count_sai1); + hws[IMX6UL_CLK_SAI2] = imx_clk_hw_gate2_shared("sai2", "sai2_podf", base + 0x7c, 30, &share_count_sai2); + hws[IMX6UL_CLK_SAI2_IPG] = imx_clk_hw_gate2_shared("sai2_ipg", "ipg", base + 0x7c, 30, &share_count_sai2); + + /* CCGR6 */ + hws[IMX6UL_CLK_USBOH3] = imx_clk_hw_gate2("usboh3", "ipg", base + 0x80, 0); + hws[IMX6UL_CLK_USDHC1] = imx_clk_hw_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2); + hws[IMX6UL_CLK_USDHC2] = imx_clk_hw_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4); + if (clk_on_imx6ul()) { + hws[IMX6UL_CLK_SIM1] = imx_clk_hw_gate2("sim1", "sim_sel", base + 0x80, 6); + hws[IMX6UL_CLK_SIM2] = imx_clk_hw_gate2("sim2", "sim_sel", base + 0x80, 8); + } + hws[IMX6UL_CLK_EIM] = imx_clk_hw_gate2("eim", "eim_slow_podf", base + 0x80, 10); + hws[IMX6UL_CLK_PWM8] = imx_clk_hw_gate2("pwm8", "perclk", base + 0x80, 16); + hws[IMX6UL_CLK_UART8_IPG] = imx_clk_hw_gate2("uart8_ipg", "ipg", base + 0x80, 14); + hws[IMX6UL_CLK_UART8_SERIAL] = imx_clk_hw_gate2("uart8_serial", "uart_podf", base + 0x80, 14); + hws[IMX6UL_CLK_WDOG3] = imx_clk_hw_gate2("wdog3", "ipg", base + 0x80, 20); + hws[IMX6UL_CLK_I2C4] = imx_clk_hw_gate2("i2c4", "perclk", base + 0x80, 24); + hws[IMX6UL_CLK_PWM5] = imx_clk_hw_gate2("pwm5", "perclk", base + 0x80, 26); + hws[IMX6UL_CLK_PWM6] = imx_clk_hw_gate2("pwm6", "perclk", base + 0x80, 28); + hws[IMX6UL_CLK_PWM7] = imx_clk_hw_gate2("pwm7", "perclk", base + 0x80, 30); + + /* CCOSR */ + hws[IMX6UL_CLK_CKO1] = imx_clk_hw_gate("cko1", "cko1_podf", base + 0x60, 7); + hws[IMX6UL_CLK_CKO2] = imx_clk_hw_gate("cko2", "cko2_podf", base + 0x60, 24); + + /* mask handshake of mmdc */ + imx_mmdc_mask_handshake(base, 0); + + imx_check_clk_hws(hws, IMX6UL_CLK_END); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + + /* + * Lower the AHB clock rate before changing the parent clock source, + * as AHB clock rate can NOT be higher than 133MHz, but its parent + * will be switched from 396MHz PFD to 528MHz PLL in order to increase + * AXI clock rate, so we need to lower AHB rate first to make sure at + * any time, AHB rate is <= 133MHz. + */ + clk_set_rate(hws[IMX6UL_CLK_AHB]->clk, 99000000); + + /* Change periph_pre clock to pll2_bus to adjust AXI rate to 264MHz */ + clk_set_parent(hws[IMX6UL_CLK_PERIPH_CLK2_SEL]->clk, hws[IMX6UL_CLK_OSC]->clk); + clk_set_parent(hws[IMX6UL_CLK_PERIPH]->clk, hws[IMX6UL_CLK_PERIPH_CLK2]->clk); + clk_set_parent(hws[IMX6UL_CLK_PERIPH_PRE]->clk, hws[IMX6UL_CLK_PLL2_BUS]->clk); + clk_set_parent(hws[IMX6UL_CLK_PERIPH]->clk, hws[IMX6UL_CLK_PERIPH_PRE]->clk); + + /* Make sure AHB rate is 132MHz */ + clk_set_rate(hws[IMX6UL_CLK_AHB]->clk, 132000000); + + /* set perclk to from OSC */ + clk_set_parent(hws[IMX6UL_CLK_PERCLK_SEL]->clk, hws[IMX6UL_CLK_OSC]->clk); + + clk_set_rate(hws[IMX6UL_CLK_ENET_REF]->clk, 50000000); + clk_set_rate(hws[IMX6UL_CLK_ENET2_REF]->clk, 50000000); + clk_set_rate(hws[IMX6UL_CLK_CSI]->clk, 24000000); + + if (clk_on_imx6ull()) + clk_prepare_enable(hws[IMX6UL_CLK_AIPSTZ3]->clk); + + if (IS_ENABLED(CONFIG_USB_MXS_PHY)) { + clk_prepare_enable(hws[IMX6UL_CLK_USBPHY1_GATE]->clk); + clk_prepare_enable(hws[IMX6UL_CLK_USBPHY2_GATE]->clk); + } + + clk_set_parent(hws[IMX6UL_CLK_CAN_SEL]->clk, hws[IMX6UL_CLK_PLL3_80M]->clk); + if (clk_on_imx6ul()) + clk_set_parent(hws[IMX6UL_CLK_SIM_PRE_SEL]->clk, hws[IMX6UL_CLK_PLL3_USB_OTG]->clk); + else if (clk_on_imx6ull()) + clk_set_parent(hws[IMX6ULL_CLK_EPDC_PRE_SEL]->clk, hws[IMX6UL_CLK_PLL3_PFD2]->clk); + + clk_set_parent(hws[IMX6UL_CLK_ENFC_SEL]->clk, hws[IMX6UL_CLK_PLL2_PFD2]->clk); +} + +CLK_OF_DECLARE(imx6ul, "fsl,imx6ul-ccm", imx6ul_clocks_init); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c new file mode 100644 index 000000000..cbf8131c6 --- /dev/null +++ b/drivers/clk/imx/clk-imx7d.c @@ -0,0 +1,888 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2014-2015 Freescale Semiconductor, Inc. + */ + +#include <dt-bindings/clock/imx7d-clock.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/types.h> + +#include "clk.h" + +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_nand; +static u32 share_count_enet1; +static u32 share_count_enet2; + +static const struct clk_div_table test_div_table[] = { + { .val = 3, .div = 1, }, + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 4, }, + { } +}; + +static const struct clk_div_table post_div_table[] = { + { .val = 3, .div = 4, }, + { .val = 2, .div = 1, }, + { .val = 1, .div = 2, }, + { .val = 0, .div = 1, }, + { } +}; + +static const char *arm_a7_sel[] = { "osc", "pll_arm_main_clk", + "pll_enet_500m_clk", "pll_dram_main_clk", + "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_audio_post_div", + "pll_usb_main_clk", }; + +static const char *arm_m4_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_250m_clk", "pll_sys_pfd2_270m_clk", + "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_post_div", + "pll_usb_main_clk", }; + +static const char *axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk", + "pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd5_clk", + "pll_audio_post_div", "pll_video_post_div", "pll_sys_pfd7_clk", }; + +static const char *disp_axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk", + "pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd6_clk", + "pll_sys_pfd7_clk", "pll_audio_post_div", "pll_video_post_div", }; + +static const char *enet_axi_sel[] = { "osc", "pll_sys_pfd2_270m_clk", + "pll_dram_533m_clk", "pll_enet_250m_clk", + "pll_sys_main_240m_clk", "pll_audio_post_div", "pll_video_post_div", + "pll_sys_pfd4_clk", }; + +static const char *nand_usdhc_bus_sel[] = { "osc", "pll_sys_pfd2_270m_clk", + "pll_dram_533m_clk", "pll_sys_main_240m_clk", + "pll_sys_pfd2_135m_clk", "pll_sys_pfd6_clk", "pll_enet_250m_clk", + "pll_audio_post_div", }; + +static const char *ahb_channel_sel[] = { "osc", "pll_sys_pfd2_270m_clk", + "pll_dram_533m_clk", "pll_sys_pfd0_392m_clk", + "pll_enet_250m_clk", "pll_usb_main_clk", "pll_audio_post_div", + "pll_video_post_div", }; + +static const char *dram_phym_sel[] = { "pll_dram_main_clk", + "dram_phym_alt_clk", }; + +static const char *dram_sel[] = { "pll_dram_main_clk", + "dram_alt_root_clk", }; + +static const char *dram_phym_alt_sel[] = { "osc", "pll_dram_533m_clk", + "pll_sys_main_clk", "pll_enet_500m_clk", + "pll_usb_main_clk", "pll_sys_pfd7_clk", "pll_audio_post_div", + "pll_video_post_div", }; + +static const char *dram_alt_sel[] = { "osc", "pll_dram_533m_clk", + "pll_sys_main_clk", "pll_enet_500m_clk", + "pll_enet_250m_clk", "pll_sys_pfd0_392m_clk", + "pll_audio_post_div", "pll_sys_pfd2_270m_clk", }; + +static const char *usb_hsic_sel[] = { "osc", "pll_sys_main_clk", + "pll_usb_main_clk", "pll_sys_pfd3_clk", "pll_sys_pfd4_clk", + "pll_sys_pfd5_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", }; + +static const char *pcie_ctrl_sel[] = { "osc", "pll_enet_250m_clk", + "pll_sys_main_240m_clk", "pll_sys_pfd2_270m_clk", + "pll_dram_533m_clk", "pll_enet_500m_clk", + "pll_sys_pfd1_332m_clk", "pll_sys_pfd6_clk", }; + +static const char *pcie_phy_sel[] = { "osc", "pll_enet_100m_clk", + "pll_enet_500m_clk", "ext_clk_1", "ext_clk_2", "ext_clk_3", + "ext_clk_4", "pll_sys_pfd0_392m_clk", }; + +static const char *epdc_pixel_sel[] = { "osc", "pll_sys_pfd1_332m_clk", + "pll_dram_533m_clk", "pll_sys_main_clk", "pll_sys_pfd5_clk", + "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", "pll_video_post_div", }; + +static const char *lcdif_pixel_sel[] = { "osc", "pll_sys_pfd5_clk", + "pll_dram_533m_clk", "ext_clk_3", "pll_sys_pfd4_clk", + "pll_sys_pfd2_270m_clk", "pll_video_post_div", + "pll_usb_main_clk", }; + +static const char *mipi_dsi_sel[] = { "osc", "pll_sys_pfd5_clk", + "pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk", + "pll_dram_533m_clk", "pll_video_post_div", "pll_audio_post_div", }; + +static const char *mipi_csi_sel[] = { "osc", "pll_sys_pfd4_clk", + "pll_sys_pfd3_clk", "pll_sys_main_clk", "pll_sys_pfd0_196m_clk", + "pll_dram_533m_clk", "pll_video_post_div", "pll_audio_post_div", }; + +static const char *mipi_dphy_sel[] = { "osc", "pll_sys_main_120m_clk", + "pll_dram_533m_clk", "pll_sys_pfd5_clk", "ref_1m_clk", "ext_clk_2", + "pll_video_post_div", "ext_clk_3", }; + +static const char *sai1_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div", + "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", }; + +static const char *sai2_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div", + "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_2", }; + +static const char *sai3_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div", + "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_clk_3", }; + +static const char *spdif_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_audio_post_div", "pll_dram_533m_clk", "pll_video_post_div", + "pll_sys_pfd4_clk", "pll_enet_125m_clk", "ext_3_clk", }; + +static const char *enet1_ref_sel[] = { "osc", "pll_enet_125m_clk", + "pll_enet_50m_clk", "pll_enet_25m_clk", + "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_post_div", + "ext_clk_4", }; + +static const char *enet1_time_sel[] = { "osc", "pll_enet_100m_clk", + "pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3", + "ext_clk_4", "pll_video_post_div", }; + +static const char *enet2_ref_sel[] = { "osc", "pll_enet_125m_clk", + "pll_enet_50m_clk", "pll_enet_25m_clk", + "pll_sys_main_120m_clk", "pll_audio_post_div", "pll_video_post_div", + "ext_clk_4", }; + +static const char *enet2_time_sel[] = { "osc", "pll_enet_100m_clk", + "pll_audio_post_div", "ext_clk_1", "ext_clk_2", "ext_clk_3", + "ext_clk_4", "pll_video_post_div", }; + +static const char *enet_phy_ref_sel[] = { "osc", "pll_enet_25m_clk", + "pll_enet_50m_clk", "pll_enet_125m_clk", + "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_post_div", + "pll_sys_pfd3_clk", }; + +static const char *eim_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_sys_main_120m_clk", "pll_dram_533m_clk", + "pll_sys_pfd2_270m_clk", "pll_sys_pfd3_clk", "pll_enet_125m_clk", + "pll_usb_main_clk", }; + +static const char *nand_sel[] = { "osc", "pll_sys_main_clk", + "pll_dram_533m_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd3_clk", + "pll_enet_500m_clk", "pll_enet_250m_clk", + "pll_video_post_div", }; + +static const char *qspi_sel[] = { "osc", "pll_sys_pfd4_clk", + "pll_dram_533m_clk", "pll_enet_500m_clk", "pll_sys_pfd3_clk", + "pll_sys_pfd2_270m_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", }; + +static const char *usdhc1_sel[] = { "osc", "pll_sys_pfd0_392m_clk", + "pll_dram_533m_clk", "pll_enet_500m_clk", "pll_sys_pfd4_clk", + "pll_sys_pfd2_270m_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", }; + +static const char *usdhc2_sel[] = { "osc", "pll_sys_pfd0_392m_clk", + "pll_dram_533m_clk", "pll_enet_500m_clk", "pll_sys_pfd4_clk", + "pll_sys_pfd2_270m_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", }; + +static const char *usdhc3_sel[] = { "osc", "pll_sys_pfd0_392m_clk", + "pll_dram_533m_clk", "pll_enet_500m_clk", "pll_sys_pfd4_clk", + "pll_sys_pfd2_270m_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", }; + +static const char *can1_sel[] = { "osc", "pll_sys_main_120m_clk", + "pll_dram_533m_clk", "pll_sys_main_clk", + "pll_enet_40m_clk", "pll_usb_main_clk", "ext_clk_1", + "ext_clk_4", }; + +static const char *can2_sel[] = { "osc", "pll_sys_main_120m_clk", + "pll_dram_533m_clk", "pll_sys_main_clk", + "pll_enet_40m_clk", "pll_usb_main_clk", "ext_clk_1", + "ext_clk_3", }; + +static const char *i2c1_sel[] = { "osc", "pll_sys_main_120m_clk", + "pll_enet_50m_clk", "pll_dram_533m_clk", + "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk", + "pll_sys_pfd2_135m_clk", }; + +static const char *i2c2_sel[] = { "osc", "pll_sys_main_120m_clk", + "pll_enet_50m_clk", "pll_dram_533m_clk", + "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk", + "pll_sys_pfd2_135m_clk", }; + +static const char *i2c3_sel[] = { "osc", "pll_sys_main_120m_clk", + "pll_enet_50m_clk", "pll_dram_533m_clk", + "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk", + "pll_sys_pfd2_135m_clk", }; + +static const char *i2c4_sel[] = { "osc", "pll_sys_main_120m_clk", + "pll_enet_50m_clk", "pll_dram_533m_clk", + "pll_audio_post_div", "pll_video_post_div", "pll_usb_main_clk", + "pll_sys_pfd2_135m_clk", }; + +static const char *uart1_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_enet_100m_clk", + "pll_sys_main_clk", "ext_clk_2", "ext_clk_4", + "pll_usb_main_clk", }; + +static const char *uart2_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_enet_100m_clk", + "pll_sys_main_clk", "ext_clk_2", "ext_clk_3", + "pll_usb_main_clk", }; + +static const char *uart3_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_enet_100m_clk", + "pll_sys_main_clk", "ext_clk_2", "ext_clk_4", + "pll_usb_main_clk", }; + +static const char *uart4_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_enet_100m_clk", + "pll_sys_main_clk", "ext_clk_2", "ext_clk_3", + "pll_usb_main_clk", }; + +static const char *uart5_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_enet_100m_clk", + "pll_sys_main_clk", "ext_clk_2", "ext_clk_4", + "pll_usb_main_clk", }; + +static const char *uart6_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_enet_100m_clk", + "pll_sys_main_clk", "ext_clk_2", "ext_clk_3", + "pll_usb_main_clk", }; + +static const char *uart7_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_enet_100m_clk", + "pll_sys_main_clk", "ext_clk_2", "ext_clk_4", + "pll_usb_main_clk", }; + +static const char *ecspi1_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_sys_main_120m_clk", + "pll_sys_main_clk", "pll_sys_pfd4_clk", "pll_enet_250m_clk", + "pll_usb_main_clk", }; + +static const char *ecspi2_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_sys_main_120m_clk", + "pll_sys_main_clk", "pll_sys_pfd4_clk", "pll_enet_250m_clk", + "pll_usb_main_clk", }; + +static const char *ecspi3_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_sys_main_120m_clk", + "pll_sys_main_clk", "pll_sys_pfd4_clk", "pll_enet_250m_clk", + "pll_usb_main_clk", }; + +static const char *ecspi4_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_enet_40m_clk", "pll_sys_main_120m_clk", + "pll_sys_main_clk", "pll_sys_pfd4_clk", "pll_enet_250m_clk", + "pll_usb_main_clk", }; + +static const char *pwm1_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", + "ext_clk_1", "ref_1m_clk", "pll_video_post_div", }; + +static const char *pwm2_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", + "ext_clk_1", "ref_1m_clk", "pll_video_post_div", }; + +static const char *pwm3_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", + "ext_clk_2", "ref_1m_clk", "pll_video_post_div", }; + +static const char *pwm4_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", + "ext_clk_2", "ref_1m_clk", "pll_video_post_div", }; + +static const char *flextimer1_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", + "ext_clk_3", "ref_1m_clk", "pll_video_post_div", }; + +static const char *flextimer2_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_main_120m_clk", "pll_enet_40m_clk", "pll_audio_post_div", + "ext_clk_3", "ref_1m_clk", "pll_video_post_div", }; + +static const char *sim1_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_sys_main_120m_clk", "pll_dram_533m_clk", + "pll_usb_main_clk", "pll_audio_post_div", "pll_enet_125m_clk", + "pll_sys_pfd7_clk", }; + +static const char *sim2_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_sys_main_120m_clk", "pll_dram_533m_clk", + "pll_usb_main_clk", "pll_video_post_div", "pll_enet_125m_clk", + "pll_sys_pfd7_clk", }; + +static const char *gpt1_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div", + "ref_1m_clk", "pll_audio_post_div", "ext_clk_1", }; + +static const char *gpt2_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div", + "ref_1m_clk", "pll_audio_post_div", "ext_clk_2", }; + +static const char *gpt3_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div", + "ref_1m_clk", "pll_audio_post_div", "ext_clk_3", }; + +static const char *gpt4_sel[] = { "osc", "pll_enet_100m_clk", + "pll_sys_pfd0_392m_clk", "pll_enet_40m_clk", "pll_video_post_div", + "ref_1m_clk", "pll_audio_post_div", "ext_clk_4", }; + +static const char *trace_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_sys_main_120m_clk", "pll_dram_533m_clk", + "pll_enet_125m_clk", "pll_usb_main_clk", "ext_clk_2", + "ext_clk_3", }; + +static const char *wdog_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_sys_main_120m_clk", "pll_dram_533m_clk", + "pll_enet_125m_clk", "pll_usb_main_clk", "ref_1m_clk", + "pll_sys_pfd1_166m_clk", }; + +static const char *csi_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_sys_main_120m_clk", "pll_dram_533m_clk", + "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_post_div", + "pll_usb_main_clk", }; + +static const char *audio_mclk_sel[] = { "osc", "pll_sys_pfd2_135m_clk", + "pll_sys_main_120m_clk", "pll_dram_533m_clk", + "pll_enet_125m_clk", "pll_audio_post_div", "pll_video_post_div", + "pll_usb_main_clk", }; + +static const char *wrclk_sel[] = { "osc", "pll_enet_40m_clk", + "pll_dram_533m_clk", "pll_usb_main_clk", + "pll_sys_main_240m_clk", "pll_sys_pfd2_270m_clk", + "pll_enet_500m_clk", "pll_sys_pfd7_clk", }; + +static const char *clko1_sel[] = { "osc", "pll_sys_main_clk", + "pll_sys_main_240m_clk", "pll_sys_pfd0_196m_clk", "pll_sys_pfd3_clk", + "pll_enet_500m_clk", "pll_dram_533m_clk", "ref_1m_clk", }; + +static const char *clko2_sel[] = { "osc", "pll_sys_main_240m_clk", + "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_166m_clk", "pll_sys_pfd4_clk", + "pll_audio_post_div", "pll_video_post_div", "ckil", }; + +static const char *lvds1_sel[] = { "pll_arm_main_clk", + "pll_sys_main_clk", "pll_sys_pfd0_392m_clk", "pll_sys_pfd1_332m_clk", + "pll_sys_pfd2_270m_clk", "pll_sys_pfd3_clk", "pll_sys_pfd4_clk", + "pll_sys_pfd5_clk", "pll_sys_pfd6_clk", "pll_sys_pfd7_clk", + "pll_audio_post_div", "pll_video_post_div", "pll_enet_500m_clk", + "pll_enet_250m_clk", "pll_enet_125m_clk", "pll_enet_100m_clk", + "pll_enet_50m_clk", "pll_enet_40m_clk", "pll_enet_25m_clk", + "pll_dram_main_clk", }; + +static const char *pll_bypass_src_sel[] = { "osc", "dummy", }; +static const char *pll_arm_bypass_sel[] = { "pll_arm_main", "pll_arm_main_src", }; +static const char *pll_dram_bypass_sel[] = { "pll_dram_main", "pll_dram_main_src", }; +static const char *pll_sys_bypass_sel[] = { "pll_sys_main", "pll_sys_main_src", }; +static const char *pll_enet_bypass_sel[] = { "pll_enet_main", "pll_enet_main_src", }; +static const char *pll_audio_bypass_sel[] = { "pll_audio_main", "pll_audio_main_src", }; +static const char *pll_video_bypass_sel[] = { "pll_video_main", "pll_video_main_src", }; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; + +static void __init imx7d_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + void __iomem *base; + + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + IMX7D_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return; + + clk_hw_data->num = IMX7D_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX7D_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + hws[IMX7D_OSC_24M_CLK] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); + hws[IMX7D_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop"); + base = of_iomap(np, 0); + WARN_ON(!base); + of_node_put(np); + + hws[IMX7D_PLL_ARM_MAIN_SRC] = imx_clk_hw_mux("pll_arm_main_src", base + 0x60, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); + hws[IMX7D_PLL_DRAM_MAIN_SRC] = imx_clk_hw_mux("pll_dram_main_src", base + 0x70, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); + hws[IMX7D_PLL_SYS_MAIN_SRC] = imx_clk_hw_mux("pll_sys_main_src", base + 0xb0, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); + hws[IMX7D_PLL_ENET_MAIN_SRC] = imx_clk_hw_mux("pll_enet_main_src", base + 0xe0, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); + hws[IMX7D_PLL_AUDIO_MAIN_SRC] = imx_clk_hw_mux("pll_audio_main_src", base + 0xf0, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); + hws[IMX7D_PLL_VIDEO_MAIN_SRC] = imx_clk_hw_mux("pll_video_main_src", base + 0x130, 14, 2, pll_bypass_src_sel, ARRAY_SIZE(pll_bypass_src_sel)); + + hws[IMX7D_PLL_ARM_MAIN] = imx_clk_hw_pllv3(IMX_PLLV3_SYS, "pll_arm_main", "osc", base + 0x60, 0x7f); + hws[IMX7D_PLL_DRAM_MAIN] = imx_clk_hw_pllv3(IMX_PLLV3_DDR_IMX7, "pll_dram_main", "osc", base + 0x70, 0x7f); + hws[IMX7D_PLL_SYS_MAIN] = imx_clk_hw_pllv3(IMX_PLLV3_GENERIC, "pll_sys_main", "osc", base + 0xb0, 0x1); + hws[IMX7D_PLL_ENET_MAIN] = imx_clk_hw_pllv3(IMX_PLLV3_ENET_IMX7, "pll_enet_main", "osc", base + 0xe0, 0x0); + hws[IMX7D_PLL_AUDIO_MAIN] = imx_clk_hw_pllv3(IMX_PLLV3_AV_IMX7, "pll_audio_main", "osc", base + 0xf0, 0x7f); + hws[IMX7D_PLL_VIDEO_MAIN] = imx_clk_hw_pllv3(IMX_PLLV3_AV_IMX7, "pll_video_main", "osc", base + 0x130, 0x7f); + + hws[IMX7D_PLL_ARM_MAIN_BYPASS] = imx_clk_hw_mux_flags("pll_arm_main_bypass", base + 0x60, 16, 1, pll_arm_bypass_sel, ARRAY_SIZE(pll_arm_bypass_sel), CLK_SET_RATE_PARENT); + hws[IMX7D_PLL_DRAM_MAIN_BYPASS] = imx_clk_hw_mux_flags("pll_dram_main_bypass", base + 0x70, 16, 1, pll_dram_bypass_sel, ARRAY_SIZE(pll_dram_bypass_sel), CLK_SET_RATE_PARENT); + hws[IMX7D_PLL_SYS_MAIN_BYPASS] = imx_clk_hw_mux_flags("pll_sys_main_bypass", base + 0xb0, 16, 1, pll_sys_bypass_sel, ARRAY_SIZE(pll_sys_bypass_sel), CLK_SET_RATE_PARENT); + hws[IMX7D_PLL_ENET_MAIN_BYPASS] = imx_clk_hw_mux_flags("pll_enet_main_bypass", base + 0xe0, 16, 1, pll_enet_bypass_sel, ARRAY_SIZE(pll_enet_bypass_sel), CLK_SET_RATE_PARENT); + hws[IMX7D_PLL_AUDIO_MAIN_BYPASS] = imx_clk_hw_mux_flags("pll_audio_main_bypass", base + 0xf0, 16, 1, pll_audio_bypass_sel, ARRAY_SIZE(pll_audio_bypass_sel), CLK_SET_RATE_PARENT); + hws[IMX7D_PLL_VIDEO_MAIN_BYPASS] = imx_clk_hw_mux_flags("pll_video_main_bypass", base + 0x130, 16, 1, pll_video_bypass_sel, ARRAY_SIZE(pll_video_bypass_sel), CLK_SET_RATE_PARENT); + + hws[IMX7D_PLL_ARM_MAIN_CLK] = imx_clk_hw_gate("pll_arm_main_clk", "pll_arm_main_bypass", base + 0x60, 13); + hws[IMX7D_PLL_DRAM_MAIN_CLK] = imx_clk_hw_gate("pll_dram_main_clk", "pll_dram_test_div", base + 0x70, 13); + hws[IMX7D_PLL_SYS_MAIN_CLK] = imx_clk_hw_gate("pll_sys_main_clk", "pll_sys_main_bypass", base + 0xb0, 13); + hws[IMX7D_PLL_AUDIO_MAIN_CLK] = imx_clk_hw_gate("pll_audio_main_clk", "pll_audio_main_bypass", base + 0xf0, 13); + hws[IMX7D_PLL_VIDEO_MAIN_CLK] = imx_clk_hw_gate("pll_video_main_clk", "pll_video_main_bypass", base + 0x130, 13); + + hws[IMX7D_PLL_DRAM_TEST_DIV] = clk_hw_register_divider_table(NULL, "pll_dram_test_div", "pll_dram_main_bypass", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x70, 21, 2, 0, test_div_table, &imx_ccm_lock); + hws[IMX7D_PLL_AUDIO_TEST_DIV] = clk_hw_register_divider_table(NULL, "pll_audio_test_div", "pll_audio_main_clk", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 19, 2, 0, test_div_table, &imx_ccm_lock); + hws[IMX7D_PLL_AUDIO_POST_DIV] = clk_hw_register_divider_table(NULL, "pll_audio_post_div", "pll_audio_test_div", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0xf0, 22, 2, 0, post_div_table, &imx_ccm_lock); + hws[IMX7D_PLL_VIDEO_TEST_DIV] = clk_hw_register_divider_table(NULL, "pll_video_test_div", "pll_video_main_clk", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x130, 19, 2, 0, test_div_table, &imx_ccm_lock); + hws[IMX7D_PLL_VIDEO_POST_DIV] = clk_hw_register_divider_table(NULL, "pll_video_post_div", "pll_video_test_div", + CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, base + 0x130, 22, 2, 0, post_div_table, &imx_ccm_lock); + + hws[IMX7D_PLL_SYS_PFD0_392M_CLK] = imx_clk_hw_pfd("pll_sys_pfd0_392m_clk", "pll_sys_main_clk", base + 0xc0, 0); + hws[IMX7D_PLL_SYS_PFD1_332M_CLK] = imx_clk_hw_pfd("pll_sys_pfd1_332m_clk", "pll_sys_main_clk", base + 0xc0, 1); + hws[IMX7D_PLL_SYS_PFD2_270M_CLK] = imx_clk_hw_pfd("pll_sys_pfd2_270m_clk", "pll_sys_main_clk", base + 0xc0, 2); + + hws[IMX7D_PLL_SYS_PFD3_CLK] = imx_clk_hw_pfd("pll_sys_pfd3_clk", "pll_sys_main_clk", base + 0xc0, 3); + hws[IMX7D_PLL_SYS_PFD4_CLK] = imx_clk_hw_pfd("pll_sys_pfd4_clk", "pll_sys_main_clk", base + 0xd0, 0); + hws[IMX7D_PLL_SYS_PFD5_CLK] = imx_clk_hw_pfd("pll_sys_pfd5_clk", "pll_sys_main_clk", base + 0xd0, 1); + hws[IMX7D_PLL_SYS_PFD6_CLK] = imx_clk_hw_pfd("pll_sys_pfd6_clk", "pll_sys_main_clk", base + 0xd0, 2); + hws[IMX7D_PLL_SYS_PFD7_CLK] = imx_clk_hw_pfd("pll_sys_pfd7_clk", "pll_sys_main_clk", base + 0xd0, 3); + + hws[IMX7D_PLL_SYS_MAIN_480M] = imx_clk_hw_fixed_factor("pll_sys_main_480m", "pll_sys_main_clk", 1, 1); + hws[IMX7D_PLL_SYS_MAIN_240M] = imx_clk_hw_fixed_factor("pll_sys_main_240m", "pll_sys_main_clk", 1, 2); + hws[IMX7D_PLL_SYS_MAIN_120M] = imx_clk_hw_fixed_factor("pll_sys_main_120m", "pll_sys_main_clk", 1, 4); + hws[IMX7D_PLL_DRAM_MAIN_533M] = imx_clk_hw_fixed_factor("pll_dram_533m", "pll_dram_main_clk", 1, 2); + + hws[IMX7D_PLL_SYS_MAIN_480M_CLK] = imx_clk_hw_gate_dis_flags("pll_sys_main_480m_clk", "pll_sys_main_480m", base + 0xb0, 4, CLK_IS_CRITICAL); + hws[IMX7D_PLL_SYS_MAIN_240M_CLK] = imx_clk_hw_gate_dis("pll_sys_main_240m_clk", "pll_sys_main_240m", base + 0xb0, 5); + hws[IMX7D_PLL_SYS_MAIN_120M_CLK] = imx_clk_hw_gate_dis("pll_sys_main_120m_clk", "pll_sys_main_120m", base + 0xb0, 6); + hws[IMX7D_PLL_DRAM_MAIN_533M_CLK] = imx_clk_hw_gate("pll_dram_533m_clk", "pll_dram_533m", base + 0x70, 12); + + hws[IMX7D_PLL_SYS_PFD0_196M] = imx_clk_hw_fixed_factor("pll_sys_pfd0_196m", "pll_sys_pfd0_392m_clk", 1, 2); + hws[IMX7D_PLL_SYS_PFD1_166M] = imx_clk_hw_fixed_factor("pll_sys_pfd1_166m", "pll_sys_pfd1_332m_clk", 1, 2); + hws[IMX7D_PLL_SYS_PFD2_135M] = imx_clk_hw_fixed_factor("pll_sys_pfd2_135m", "pll_sys_pfd2_270m_clk", 1, 2); + + hws[IMX7D_PLL_SYS_PFD0_196M_CLK] = imx_clk_hw_gate_dis("pll_sys_pfd0_196m_clk", "pll_sys_pfd0_196m", base + 0xb0, 26); + hws[IMX7D_PLL_SYS_PFD1_166M_CLK] = imx_clk_hw_gate_dis("pll_sys_pfd1_166m_clk", "pll_sys_pfd1_166m", base + 0xb0, 27); + hws[IMX7D_PLL_SYS_PFD2_135M_CLK] = imx_clk_hw_gate_dis("pll_sys_pfd2_135m_clk", "pll_sys_pfd2_135m", base + 0xb0, 28); + + hws[IMX7D_PLL_ENET_MAIN_CLK] = imx_clk_hw_fixed_factor("pll_enet_main_clk", "pll_enet_main_bypass", 1, 1); + hws[IMX7D_PLL_ENET_MAIN_500M] = imx_clk_hw_fixed_factor("pll_enet_500m", "pll_enet_main_clk", 1, 2); + hws[IMX7D_PLL_ENET_MAIN_250M] = imx_clk_hw_fixed_factor("pll_enet_250m", "pll_enet_main_clk", 1, 4); + hws[IMX7D_PLL_ENET_MAIN_125M] = imx_clk_hw_fixed_factor("pll_enet_125m", "pll_enet_main_clk", 1, 8); + hws[IMX7D_PLL_ENET_MAIN_100M] = imx_clk_hw_fixed_factor("pll_enet_100m", "pll_enet_main_clk", 1, 10); + hws[IMX7D_PLL_ENET_MAIN_50M] = imx_clk_hw_fixed_factor("pll_enet_50m", "pll_enet_main_clk", 1, 20); + hws[IMX7D_PLL_ENET_MAIN_40M] = imx_clk_hw_fixed_factor("pll_enet_40m", "pll_enet_main_clk", 1, 25); + hws[IMX7D_PLL_ENET_MAIN_25M] = imx_clk_hw_fixed_factor("pll_enet_25m", "pll_enet_main_clk", 1, 40); + + hws[IMX7D_PLL_ENET_MAIN_500M_CLK] = imx_clk_hw_gate("pll_enet_500m_clk", "pll_enet_500m", base + 0xe0, 12); + hws[IMX7D_PLL_ENET_MAIN_250M_CLK] = imx_clk_hw_gate("pll_enet_250m_clk", "pll_enet_250m", base + 0xe0, 11); + hws[IMX7D_PLL_ENET_MAIN_125M_CLK] = imx_clk_hw_gate("pll_enet_125m_clk", "pll_enet_125m", base + 0xe0, 10); + hws[IMX7D_PLL_ENET_MAIN_100M_CLK] = imx_clk_hw_gate("pll_enet_100m_clk", "pll_enet_100m", base + 0xe0, 9); + hws[IMX7D_PLL_ENET_MAIN_50M_CLK] = imx_clk_hw_gate("pll_enet_50m_clk", "pll_enet_50m", base + 0xe0, 8); + hws[IMX7D_PLL_ENET_MAIN_40M_CLK] = imx_clk_hw_gate("pll_enet_40m_clk", "pll_enet_40m", base + 0xe0, 7); + hws[IMX7D_PLL_ENET_MAIN_25M_CLK] = imx_clk_hw_gate("pll_enet_25m_clk", "pll_enet_25m", base + 0xe0, 6); + + hws[IMX7D_LVDS1_OUT_SEL] = imx_clk_hw_mux("lvds1_sel", base + 0x170, 0, 5, lvds1_sel, ARRAY_SIZE(lvds1_sel)); + hws[IMX7D_LVDS1_OUT_CLK] = imx_clk_hw_gate_exclusive("lvds1_out", "lvds1_sel", base + 0x170, 5, BIT(6)); + + np = ccm_node; + base = of_iomap(np, 0); + WARN_ON(!base); + + hws[IMX7D_ARM_A7_ROOT_SRC] = imx_clk_hw_mux2("arm_a7_src", base + 0x8000, 24, 3, arm_a7_sel, ARRAY_SIZE(arm_a7_sel)); + hws[IMX7D_ARM_M4_ROOT_SRC] = imx_clk_hw_mux2("arm_m4_src", base + 0x8080, 24, 3, arm_m4_sel, ARRAY_SIZE(arm_m4_sel)); + hws[IMX7D_MAIN_AXI_ROOT_SRC] = imx_clk_hw_mux2("axi_src", base + 0x8800, 24, 3, axi_sel, ARRAY_SIZE(axi_sel)); + hws[IMX7D_DISP_AXI_ROOT_SRC] = imx_clk_hw_mux2("disp_axi_src", base + 0x8880, 24, 3, disp_axi_sel, ARRAY_SIZE(disp_axi_sel)); + hws[IMX7D_AHB_CHANNEL_ROOT_SRC] = imx_clk_hw_mux2("ahb_src", base + 0x9000, 24, 3, ahb_channel_sel, ARRAY_SIZE(ahb_channel_sel)); + + hws[IMX7D_ENET_AXI_ROOT_SRC] = imx_clk_hw_mux2_flags("enet_axi_src", base + 0x8900, 24, 3, enet_axi_sel, ARRAY_SIZE(enet_axi_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_NAND_USDHC_BUS_ROOT_SRC] = imx_clk_hw_mux2_flags("nand_usdhc_src", base + 0x8980, 24, 3, nand_usdhc_bus_sel, ARRAY_SIZE(nand_usdhc_bus_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_DRAM_PHYM_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_phym_src", base + 0x9800, 24, 1, dram_phym_sel, ARRAY_SIZE(dram_phym_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_DRAM_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_src", base + 0x9880, 24, 1, dram_sel, ARRAY_SIZE(dram_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_DRAM_PHYM_ALT_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_phym_alt_src", base + 0xa000, 24, 3, dram_phym_alt_sel, ARRAY_SIZE(dram_phym_alt_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_DRAM_ALT_ROOT_SRC] = imx_clk_hw_mux2_flags("dram_alt_src", base + 0xa080, 24, 3, dram_alt_sel, ARRAY_SIZE(dram_alt_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_USB_HSIC_ROOT_SRC] = imx_clk_hw_mux2_flags("usb_hsic_src", base + 0xa100, 24, 3, usb_hsic_sel, ARRAY_SIZE(usb_hsic_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_PCIE_CTRL_ROOT_SRC] = imx_clk_hw_mux2_flags("pcie_ctrl_src", base + 0xa180, 24, 3, pcie_ctrl_sel, ARRAY_SIZE(pcie_ctrl_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_PCIE_PHY_ROOT_SRC] = imx_clk_hw_mux2_flags("pcie_phy_src", base + 0xa200, 24, 3, pcie_phy_sel, ARRAY_SIZE(pcie_phy_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_EPDC_PIXEL_ROOT_SRC] = imx_clk_hw_mux2_flags("epdc_pixel_src", base + 0xa280, 24, 3, epdc_pixel_sel, ARRAY_SIZE(epdc_pixel_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_LCDIF_PIXEL_ROOT_SRC] = imx_clk_hw_mux2_flags("lcdif_pixel_src", base + 0xa300, 24, 3, lcdif_pixel_sel, ARRAY_SIZE(lcdif_pixel_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_MIPI_DSI_ROOT_SRC] = imx_clk_hw_mux2_flags("mipi_dsi_src", base + 0xa380, 24, 3, mipi_dsi_sel, ARRAY_SIZE(mipi_dsi_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_MIPI_CSI_ROOT_SRC] = imx_clk_hw_mux2_flags("mipi_csi_src", base + 0xa400, 24, 3, mipi_csi_sel, ARRAY_SIZE(mipi_csi_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_MIPI_DPHY_ROOT_SRC] = imx_clk_hw_mux2_flags("mipi_dphy_src", base + 0xa480, 24, 3, mipi_dphy_sel, ARRAY_SIZE(mipi_dphy_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_SAI1_ROOT_SRC] = imx_clk_hw_mux2_flags("sai1_src", base + 0xa500, 24, 3, sai1_sel, ARRAY_SIZE(sai1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_SAI2_ROOT_SRC] = imx_clk_hw_mux2_flags("sai2_src", base + 0xa580, 24, 3, sai2_sel, ARRAY_SIZE(sai2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_SAI3_ROOT_SRC] = imx_clk_hw_mux2_flags("sai3_src", base + 0xa600, 24, 3, sai3_sel, ARRAY_SIZE(sai3_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_SPDIF_ROOT_SRC] = imx_clk_hw_mux2_flags("spdif_src", base + 0xa680, 24, 3, spdif_sel, ARRAY_SIZE(spdif_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ENET1_REF_ROOT_SRC] = imx_clk_hw_mux2_flags("enet1_ref_src", base + 0xa700, 24, 3, enet1_ref_sel, ARRAY_SIZE(enet1_ref_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ENET1_TIME_ROOT_SRC] = imx_clk_hw_mux2_flags("enet1_time_src", base + 0xa780, 24, 3, enet1_time_sel, ARRAY_SIZE(enet1_time_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ENET2_REF_ROOT_SRC] = imx_clk_hw_mux2_flags("enet2_ref_src", base + 0xa800, 24, 3, enet2_ref_sel, ARRAY_SIZE(enet2_ref_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ENET2_TIME_ROOT_SRC] = imx_clk_hw_mux2_flags("enet2_time_src", base + 0xa880, 24, 3, enet2_time_sel, ARRAY_SIZE(enet2_time_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ENET_PHY_REF_ROOT_SRC] = imx_clk_hw_mux2_flags("enet_phy_ref_src", base + 0xa900, 24, 3, enet_phy_ref_sel, ARRAY_SIZE(enet_phy_ref_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_EIM_ROOT_SRC] = imx_clk_hw_mux2_flags("eim_src", base + 0xa980, 24, 3, eim_sel, ARRAY_SIZE(eim_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_NAND_ROOT_SRC] = imx_clk_hw_mux2_flags("nand_src", base + 0xaa00, 24, 3, nand_sel, ARRAY_SIZE(nand_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_QSPI_ROOT_SRC] = imx_clk_hw_mux2_flags("qspi_src", base + 0xaa80, 24, 3, qspi_sel, ARRAY_SIZE(qspi_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_USDHC1_ROOT_SRC] = imx_clk_hw_mux2_flags("usdhc1_src", base + 0xab00, 24, 3, usdhc1_sel, ARRAY_SIZE(usdhc1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_USDHC2_ROOT_SRC] = imx_clk_hw_mux2_flags("usdhc2_src", base + 0xab80, 24, 3, usdhc2_sel, ARRAY_SIZE(usdhc2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_USDHC3_ROOT_SRC] = imx_clk_hw_mux2_flags("usdhc3_src", base + 0xac00, 24, 3, usdhc3_sel, ARRAY_SIZE(usdhc3_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_CAN1_ROOT_SRC] = imx_clk_hw_mux2_flags("can1_src", base + 0xac80, 24, 3, can1_sel, ARRAY_SIZE(can1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_CAN2_ROOT_SRC] = imx_clk_hw_mux2_flags("can2_src", base + 0xad00, 24, 3, can2_sel, ARRAY_SIZE(can2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_I2C1_ROOT_SRC] = imx_clk_hw_mux2_flags("i2c1_src", base + 0xad80, 24, 3, i2c1_sel, ARRAY_SIZE(i2c1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_I2C2_ROOT_SRC] = imx_clk_hw_mux2_flags("i2c2_src", base + 0xae00, 24, 3, i2c2_sel, ARRAY_SIZE(i2c2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_I2C3_ROOT_SRC] = imx_clk_hw_mux2_flags("i2c3_src", base + 0xae80, 24, 3, i2c3_sel, ARRAY_SIZE(i2c3_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_I2C4_ROOT_SRC] = imx_clk_hw_mux2_flags("i2c4_src", base + 0xaf00, 24, 3, i2c4_sel, ARRAY_SIZE(i2c4_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_UART1_ROOT_SRC] = imx_clk_hw_mux2_flags("uart1_src", base + 0xaf80, 24, 3, uart1_sel, ARRAY_SIZE(uart1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_UART2_ROOT_SRC] = imx_clk_hw_mux2_flags("uart2_src", base + 0xb000, 24, 3, uart2_sel, ARRAY_SIZE(uart2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_UART3_ROOT_SRC] = imx_clk_hw_mux2_flags("uart3_src", base + 0xb080, 24, 3, uart3_sel, ARRAY_SIZE(uart3_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_UART4_ROOT_SRC] = imx_clk_hw_mux2_flags("uart4_src", base + 0xb100, 24, 3, uart4_sel, ARRAY_SIZE(uart4_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_UART5_ROOT_SRC] = imx_clk_hw_mux2_flags("uart5_src", base + 0xb180, 24, 3, uart5_sel, ARRAY_SIZE(uart5_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_UART6_ROOT_SRC] = imx_clk_hw_mux2_flags("uart6_src", base + 0xb200, 24, 3, uart6_sel, ARRAY_SIZE(uart6_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_UART7_ROOT_SRC] = imx_clk_hw_mux2_flags("uart7_src", base + 0xb280, 24, 3, uart7_sel, ARRAY_SIZE(uart7_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ECSPI1_ROOT_SRC] = imx_clk_hw_mux2_flags("ecspi1_src", base + 0xb300, 24, 3, ecspi1_sel, ARRAY_SIZE(ecspi1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ECSPI2_ROOT_SRC] = imx_clk_hw_mux2_flags("ecspi2_src", base + 0xb380, 24, 3, ecspi2_sel, ARRAY_SIZE(ecspi2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ECSPI3_ROOT_SRC] = imx_clk_hw_mux2_flags("ecspi3_src", base + 0xb400, 24, 3, ecspi3_sel, ARRAY_SIZE(ecspi3_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_ECSPI4_ROOT_SRC] = imx_clk_hw_mux2_flags("ecspi4_src", base + 0xb480, 24, 3, ecspi4_sel, ARRAY_SIZE(ecspi4_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_PWM1_ROOT_SRC] = imx_clk_hw_mux2_flags("pwm1_src", base + 0xb500, 24, 3, pwm1_sel, ARRAY_SIZE(pwm1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_PWM2_ROOT_SRC] = imx_clk_hw_mux2_flags("pwm2_src", base + 0xb580, 24, 3, pwm2_sel, ARRAY_SIZE(pwm2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_PWM3_ROOT_SRC] = imx_clk_hw_mux2_flags("pwm3_src", base + 0xb600, 24, 3, pwm3_sel, ARRAY_SIZE(pwm3_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_PWM4_ROOT_SRC] = imx_clk_hw_mux2_flags("pwm4_src", base + 0xb680, 24, 3, pwm4_sel, ARRAY_SIZE(pwm4_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_FLEXTIMER1_ROOT_SRC] = imx_clk_hw_mux2_flags("flextimer1_src", base + 0xb700, 24, 3, flextimer1_sel, ARRAY_SIZE(flextimer1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_FLEXTIMER2_ROOT_SRC] = imx_clk_hw_mux2_flags("flextimer2_src", base + 0xb780, 24, 3, flextimer2_sel, ARRAY_SIZE(flextimer2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_SIM1_ROOT_SRC] = imx_clk_hw_mux2_flags("sim1_src", base + 0xb800, 24, 3, sim1_sel, ARRAY_SIZE(sim1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_SIM2_ROOT_SRC] = imx_clk_hw_mux2_flags("sim2_src", base + 0xb880, 24, 3, sim2_sel, ARRAY_SIZE(sim2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_GPT1_ROOT_SRC] = imx_clk_hw_mux2_flags("gpt1_src", base + 0xb900, 24, 3, gpt1_sel, ARRAY_SIZE(gpt1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_GPT2_ROOT_SRC] = imx_clk_hw_mux2_flags("gpt2_src", base + 0xb980, 24, 3, gpt2_sel, ARRAY_SIZE(gpt2_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_GPT3_ROOT_SRC] = imx_clk_hw_mux2_flags("gpt3_src", base + 0xba00, 24, 3, gpt3_sel, ARRAY_SIZE(gpt3_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_GPT4_ROOT_SRC] = imx_clk_hw_mux2_flags("gpt4_src", base + 0xba80, 24, 3, gpt4_sel, ARRAY_SIZE(gpt4_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_TRACE_ROOT_SRC] = imx_clk_hw_mux2_flags("trace_src", base + 0xbb00, 24, 3, trace_sel, ARRAY_SIZE(trace_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_WDOG_ROOT_SRC] = imx_clk_hw_mux2_flags("wdog_src", base + 0xbb80, 24, 3, wdog_sel, ARRAY_SIZE(wdog_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_CSI_MCLK_ROOT_SRC] = imx_clk_hw_mux2_flags("csi_mclk_src", base + 0xbc00, 24, 3, csi_mclk_sel, ARRAY_SIZE(csi_mclk_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_AUDIO_MCLK_ROOT_SRC] = imx_clk_hw_mux2_flags("audio_mclk_src", base + 0xbc80, 24, 3, audio_mclk_sel, ARRAY_SIZE(audio_mclk_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_WRCLK_ROOT_SRC] = imx_clk_hw_mux2_flags("wrclk_src", base + 0xbd00, 24, 3, wrclk_sel, ARRAY_SIZE(wrclk_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_CLKO1_ROOT_SRC] = imx_clk_hw_mux2_flags("clko1_src", base + 0xbd80, 24, 3, clko1_sel, ARRAY_SIZE(clko1_sel), CLK_SET_PARENT_GATE); + hws[IMX7D_CLKO2_ROOT_SRC] = imx_clk_hw_mux2_flags("clko2_src", base + 0xbe00, 24, 3, clko2_sel, ARRAY_SIZE(clko2_sel), CLK_SET_PARENT_GATE); + + hws[IMX7D_ARM_A7_ROOT_CG] = imx_clk_hw_gate3("arm_a7_cg", "arm_a7_src", base + 0x8000, 28); + hws[IMX7D_ARM_M4_ROOT_CG] = imx_clk_hw_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28); + hws[IMX7D_MAIN_AXI_ROOT_CG] = imx_clk_hw_gate3("axi_cg", "axi_src", base + 0x8800, 28); + hws[IMX7D_DISP_AXI_ROOT_CG] = imx_clk_hw_gate3("disp_axi_cg", "disp_axi_src", base + 0x8880, 28); + hws[IMX7D_ENET_AXI_ROOT_CG] = imx_clk_hw_gate3("enet_axi_cg", "enet_axi_src", base + 0x8900, 28); + hws[IMX7D_NAND_USDHC_BUS_ROOT_CG] = imx_clk_hw_gate3("nand_usdhc_cg", "nand_usdhc_src", base + 0x8980, 28); + hws[IMX7D_AHB_CHANNEL_ROOT_CG] = imx_clk_hw_gate3("ahb_cg", "ahb_src", base + 0x9000, 28); + hws[IMX7D_DRAM_PHYM_ROOT_CG] = imx_clk_hw_gate3("dram_phym_cg", "dram_phym_src", base + 0x9800, 28); + hws[IMX7D_DRAM_ROOT_CG] = imx_clk_hw_gate3("dram_cg", "dram_src", base + 0x9880, 28); + hws[IMX7D_DRAM_PHYM_ALT_ROOT_CG] = imx_clk_hw_gate3("dram_phym_alt_cg", "dram_phym_alt_src", base + 0xa000, 28); + hws[IMX7D_DRAM_ALT_ROOT_CG] = imx_clk_hw_gate3("dram_alt_cg", "dram_alt_src", base + 0xa080, 28); + hws[IMX7D_USB_HSIC_ROOT_CG] = imx_clk_hw_gate3("usb_hsic_cg", "usb_hsic_src", base + 0xa100, 28); + hws[IMX7D_PCIE_CTRL_ROOT_CG] = imx_clk_hw_gate3("pcie_ctrl_cg", "pcie_ctrl_src", base + 0xa180, 28); + hws[IMX7D_PCIE_PHY_ROOT_CG] = imx_clk_hw_gate3("pcie_phy_cg", "pcie_phy_src", base + 0xa200, 28); + hws[IMX7D_EPDC_PIXEL_ROOT_CG] = imx_clk_hw_gate3("epdc_pixel_cg", "epdc_pixel_src", base + 0xa280, 28); + hws[IMX7D_LCDIF_PIXEL_ROOT_CG] = imx_clk_hw_gate3("lcdif_pixel_cg", "lcdif_pixel_src", base + 0xa300, 28); + hws[IMX7D_MIPI_DSI_ROOT_CG] = imx_clk_hw_gate3("mipi_dsi_cg", "mipi_dsi_src", base + 0xa380, 28); + hws[IMX7D_MIPI_CSI_ROOT_CG] = imx_clk_hw_gate3("mipi_csi_cg", "mipi_csi_src", base + 0xa400, 28); + hws[IMX7D_MIPI_DPHY_ROOT_CG] = imx_clk_hw_gate3("mipi_dphy_cg", "mipi_dphy_src", base + 0xa480, 28); + hws[IMX7D_SAI1_ROOT_CG] = imx_clk_hw_gate3("sai1_cg", "sai1_src", base + 0xa500, 28); + hws[IMX7D_SAI2_ROOT_CG] = imx_clk_hw_gate3("sai2_cg", "sai2_src", base + 0xa580, 28); + hws[IMX7D_SAI3_ROOT_CG] = imx_clk_hw_gate3("sai3_cg", "sai3_src", base + 0xa600, 28); + hws[IMX7D_SPDIF_ROOT_CG] = imx_clk_hw_gate3("spdif_cg", "spdif_src", base + 0xa680, 28); + hws[IMX7D_ENET1_REF_ROOT_CG] = imx_clk_hw_gate3("enet1_ref_cg", "enet1_ref_src", base + 0xa700, 28); + hws[IMX7D_ENET1_TIME_ROOT_CG] = imx_clk_hw_gate3("enet1_time_cg", "enet1_time_src", base + 0xa780, 28); + hws[IMX7D_ENET2_REF_ROOT_CG] = imx_clk_hw_gate3("enet2_ref_cg", "enet2_ref_src", base + 0xa800, 28); + hws[IMX7D_ENET2_TIME_ROOT_CG] = imx_clk_hw_gate3("enet2_time_cg", "enet2_time_src", base + 0xa880, 28); + hws[IMX7D_ENET_PHY_REF_ROOT_CG] = imx_clk_hw_gate3("enet_phy_ref_cg", "enet_phy_ref_src", base + 0xa900, 28); + hws[IMX7D_EIM_ROOT_CG] = imx_clk_hw_gate3("eim_cg", "eim_src", base + 0xa980, 28); + hws[IMX7D_NAND_ROOT_CG] = imx_clk_hw_gate3("nand_cg", "nand_src", base + 0xaa00, 28); + hws[IMX7D_QSPI_ROOT_CG] = imx_clk_hw_gate3("qspi_cg", "qspi_src", base + 0xaa80, 28); + hws[IMX7D_USDHC1_ROOT_CG] = imx_clk_hw_gate3("usdhc1_cg", "usdhc1_src", base + 0xab00, 28); + hws[IMX7D_USDHC2_ROOT_CG] = imx_clk_hw_gate3("usdhc2_cg", "usdhc2_src", base + 0xab80, 28); + hws[IMX7D_USDHC3_ROOT_CG] = imx_clk_hw_gate3("usdhc3_cg", "usdhc3_src", base + 0xac00, 28); + hws[IMX7D_CAN1_ROOT_CG] = imx_clk_hw_gate3("can1_cg", "can1_src", base + 0xac80, 28); + hws[IMX7D_CAN2_ROOT_CG] = imx_clk_hw_gate3("can2_cg", "can2_src", base + 0xad00, 28); + hws[IMX7D_I2C1_ROOT_CG] = imx_clk_hw_gate3("i2c1_cg", "i2c1_src", base + 0xad80, 28); + hws[IMX7D_I2C2_ROOT_CG] = imx_clk_hw_gate3("i2c2_cg", "i2c2_src", base + 0xae00, 28); + hws[IMX7D_I2C3_ROOT_CG] = imx_clk_hw_gate3("i2c3_cg", "i2c3_src", base + 0xae80, 28); + hws[IMX7D_I2C4_ROOT_CG] = imx_clk_hw_gate3("i2c4_cg", "i2c4_src", base + 0xaf00, 28); + hws[IMX7D_UART1_ROOT_CG] = imx_clk_hw_gate3("uart1_cg", "uart1_src", base + 0xaf80, 28); + hws[IMX7D_UART2_ROOT_CG] = imx_clk_hw_gate3("uart2_cg", "uart2_src", base + 0xb000, 28); + hws[IMX7D_UART3_ROOT_CG] = imx_clk_hw_gate3("uart3_cg", "uart3_src", base + 0xb080, 28); + hws[IMX7D_UART4_ROOT_CG] = imx_clk_hw_gate3("uart4_cg", "uart4_src", base + 0xb100, 28); + hws[IMX7D_UART5_ROOT_CG] = imx_clk_hw_gate3("uart5_cg", "uart5_src", base + 0xb180, 28); + hws[IMX7D_UART6_ROOT_CG] = imx_clk_hw_gate3("uart6_cg", "uart6_src", base + 0xb200, 28); + hws[IMX7D_UART7_ROOT_CG] = imx_clk_hw_gate3("uart7_cg", "uart7_src", base + 0xb280, 28); + hws[IMX7D_ECSPI1_ROOT_CG] = imx_clk_hw_gate3("ecspi1_cg", "ecspi1_src", base + 0xb300, 28); + hws[IMX7D_ECSPI2_ROOT_CG] = imx_clk_hw_gate3("ecspi2_cg", "ecspi2_src", base + 0xb380, 28); + hws[IMX7D_ECSPI3_ROOT_CG] = imx_clk_hw_gate3("ecspi3_cg", "ecspi3_src", base + 0xb400, 28); + hws[IMX7D_ECSPI4_ROOT_CG] = imx_clk_hw_gate3("ecspi4_cg", "ecspi4_src", base + 0xb480, 28); + hws[IMX7D_PWM1_ROOT_CG] = imx_clk_hw_gate3("pwm1_cg", "pwm1_src", base + 0xb500, 28); + hws[IMX7D_PWM2_ROOT_CG] = imx_clk_hw_gate3("pwm2_cg", "pwm2_src", base + 0xb580, 28); + hws[IMX7D_PWM3_ROOT_CG] = imx_clk_hw_gate3("pwm3_cg", "pwm3_src", base + 0xb600, 28); + hws[IMX7D_PWM4_ROOT_CG] = imx_clk_hw_gate3("pwm4_cg", "pwm4_src", base + 0xb680, 28); + hws[IMX7D_FLEXTIMER1_ROOT_CG] = imx_clk_hw_gate3("flextimer1_cg", "flextimer1_src", base + 0xb700, 28); + hws[IMX7D_FLEXTIMER2_ROOT_CG] = imx_clk_hw_gate3("flextimer2_cg", "flextimer2_src", base + 0xb780, 28); + hws[IMX7D_SIM1_ROOT_CG] = imx_clk_hw_gate3("sim1_cg", "sim1_src", base + 0xb800, 28); + hws[IMX7D_SIM2_ROOT_CG] = imx_clk_hw_gate3("sim2_cg", "sim2_src", base + 0xb880, 28); + hws[IMX7D_GPT1_ROOT_CG] = imx_clk_hw_gate3("gpt1_cg", "gpt1_src", base + 0xb900, 28); + hws[IMX7D_GPT2_ROOT_CG] = imx_clk_hw_gate3("gpt2_cg", "gpt2_src", base + 0xb980, 28); + hws[IMX7D_GPT3_ROOT_CG] = imx_clk_hw_gate3("gpt3_cg", "gpt3_src", base + 0xbA00, 28); + hws[IMX7D_GPT4_ROOT_CG] = imx_clk_hw_gate3("gpt4_cg", "gpt4_src", base + 0xbA80, 28); + hws[IMX7D_TRACE_ROOT_CG] = imx_clk_hw_gate3("trace_cg", "trace_src", base + 0xbb00, 28); + hws[IMX7D_WDOG_ROOT_CG] = imx_clk_hw_gate3("wdog_cg", "wdog_src", base + 0xbb80, 28); + hws[IMX7D_CSI_MCLK_ROOT_CG] = imx_clk_hw_gate3("csi_mclk_cg", "csi_mclk_src", base + 0xbc00, 28); + hws[IMX7D_AUDIO_MCLK_ROOT_CG] = imx_clk_hw_gate3("audio_mclk_cg", "audio_mclk_src", base + 0xbc80, 28); + hws[IMX7D_WRCLK_ROOT_CG] = imx_clk_hw_gate3("wrclk_cg", "wrclk_src", base + 0xbd00, 28); + hws[IMX7D_CLKO1_ROOT_CG] = imx_clk_hw_gate3("clko1_cg", "clko1_src", base + 0xbd80, 28); + hws[IMX7D_CLKO2_ROOT_CG] = imx_clk_hw_gate3("clko2_cg", "clko2_src", base + 0xbe00, 28); + + hws[IMX7D_MAIN_AXI_ROOT_PRE_DIV] = imx_clk_hw_divider2("axi_pre_div", "axi_cg", base + 0x8800, 16, 3); + hws[IMX7D_DISP_AXI_ROOT_PRE_DIV] = imx_clk_hw_divider2("disp_axi_pre_div", "disp_axi_cg", base + 0x8880, 16, 3); + hws[IMX7D_ENET_AXI_ROOT_PRE_DIV] = imx_clk_hw_divider2("enet_axi_pre_div", "enet_axi_cg", base + 0x8900, 16, 3); + hws[IMX7D_NAND_USDHC_BUS_ROOT_PRE_DIV] = imx_clk_hw_divider2("nand_usdhc_pre_div", "nand_usdhc_cg", base + 0x8980, 16, 3); + hws[IMX7D_AHB_CHANNEL_ROOT_PRE_DIV] = imx_clk_hw_divider2("ahb_pre_div", "ahb_cg", base + 0x9000, 16, 3); + hws[IMX7D_DRAM_PHYM_ALT_ROOT_PRE_DIV] = imx_clk_hw_divider2("dram_phym_alt_pre_div", "dram_phym_alt_cg", base + 0xa000, 16, 3); + hws[IMX7D_DRAM_ALT_ROOT_PRE_DIV] = imx_clk_hw_divider2("dram_alt_pre_div", "dram_alt_cg", base + 0xa080, 16, 3); + hws[IMX7D_USB_HSIC_ROOT_PRE_DIV] = imx_clk_hw_divider2("usb_hsic_pre_div", "usb_hsic_cg", base + 0xa100, 16, 3); + hws[IMX7D_PCIE_CTRL_ROOT_PRE_DIV] = imx_clk_hw_divider2("pcie_ctrl_pre_div", "pcie_ctrl_cg", base + 0xa180, 16, 3); + hws[IMX7D_PCIE_PHY_ROOT_PRE_DIV] = imx_clk_hw_divider2("pcie_phy_pre_div", "pcie_phy_cg", base + 0xa200, 16, 3); + hws[IMX7D_EPDC_PIXEL_ROOT_PRE_DIV] = imx_clk_hw_divider2("epdc_pixel_pre_div", "epdc_pixel_cg", base + 0xa280, 16, 3); + hws[IMX7D_LCDIF_PIXEL_ROOT_PRE_DIV] = imx_clk_hw_divider2("lcdif_pixel_pre_div", "lcdif_pixel_cg", base + 0xa300, 16, 3); + hws[IMX7D_MIPI_DSI_ROOT_PRE_DIV] = imx_clk_hw_divider2("mipi_dsi_pre_div", "mipi_dsi_cg", base + 0xa380, 16, 3); + hws[IMX7D_MIPI_CSI_ROOT_PRE_DIV] = imx_clk_hw_divider2("mipi_csi_pre_div", "mipi_csi_cg", base + 0xa400, 16, 3); + hws[IMX7D_MIPI_DPHY_ROOT_PRE_DIV] = imx_clk_hw_divider2("mipi_dphy_pre_div", "mipi_dphy_cg", base + 0xa480, 16, 3); + hws[IMX7D_SAI1_ROOT_PRE_DIV] = imx_clk_hw_divider2("sai1_pre_div", "sai1_cg", base + 0xa500, 16, 3); + hws[IMX7D_SAI2_ROOT_PRE_DIV] = imx_clk_hw_divider2("sai2_pre_div", "sai2_cg", base + 0xa580, 16, 3); + hws[IMX7D_SAI3_ROOT_PRE_DIV] = imx_clk_hw_divider2("sai3_pre_div", "sai3_cg", base + 0xa600, 16, 3); + hws[IMX7D_SPDIF_ROOT_PRE_DIV] = imx_clk_hw_divider2("spdif_pre_div", "spdif_cg", base + 0xa680, 16, 3); + hws[IMX7D_ENET1_REF_ROOT_PRE_DIV] = imx_clk_hw_divider2("enet1_ref_pre_div", "enet1_ref_cg", base + 0xa700, 16, 3); + hws[IMX7D_ENET1_TIME_ROOT_PRE_DIV] = imx_clk_hw_divider2("enet1_time_pre_div", "enet1_time_cg", base + 0xa780, 16, 3); + hws[IMX7D_ENET2_REF_ROOT_PRE_DIV] = imx_clk_hw_divider2("enet2_ref_pre_div", "enet2_ref_cg", base + 0xa800, 16, 3); + hws[IMX7D_ENET2_TIME_ROOT_PRE_DIV] = imx_clk_hw_divider2("enet2_time_pre_div", "enet2_time_cg", base + 0xa880, 16, 3); + hws[IMX7D_ENET_PHY_REF_ROOT_PRE_DIV] = imx_clk_hw_divider2("enet_phy_ref_pre_div", "enet_phy_ref_cg", base + 0xa900, 16, 3); + hws[IMX7D_EIM_ROOT_PRE_DIV] = imx_clk_hw_divider2("eim_pre_div", "eim_cg", base + 0xa980, 16, 3); + hws[IMX7D_NAND_ROOT_PRE_DIV] = imx_clk_hw_divider2("nand_pre_div", "nand_cg", base + 0xaa00, 16, 3); + hws[IMX7D_QSPI_ROOT_PRE_DIV] = imx_clk_hw_divider2("qspi_pre_div", "qspi_cg", base + 0xaa80, 16, 3); + hws[IMX7D_USDHC1_ROOT_PRE_DIV] = imx_clk_hw_divider2("usdhc1_pre_div", "usdhc1_cg", base + 0xab00, 16, 3); + hws[IMX7D_USDHC2_ROOT_PRE_DIV] = imx_clk_hw_divider2("usdhc2_pre_div", "usdhc2_cg", base + 0xab80, 16, 3); + hws[IMX7D_USDHC3_ROOT_PRE_DIV] = imx_clk_hw_divider2("usdhc3_pre_div", "usdhc3_cg", base + 0xac00, 16, 3); + hws[IMX7D_CAN1_ROOT_PRE_DIV] = imx_clk_hw_divider2("can1_pre_div", "can1_cg", base + 0xac80, 16, 3); + hws[IMX7D_CAN2_ROOT_PRE_DIV] = imx_clk_hw_divider2("can2_pre_div", "can2_cg", base + 0xad00, 16, 3); + hws[IMX7D_I2C1_ROOT_PRE_DIV] = imx_clk_hw_divider2("i2c1_pre_div", "i2c1_cg", base + 0xad80, 16, 3); + hws[IMX7D_I2C2_ROOT_PRE_DIV] = imx_clk_hw_divider2("i2c2_pre_div", "i2c2_cg", base + 0xae00, 16, 3); + hws[IMX7D_I2C3_ROOT_PRE_DIV] = imx_clk_hw_divider2("i2c3_pre_div", "i2c3_cg", base + 0xae80, 16, 3); + hws[IMX7D_I2C4_ROOT_PRE_DIV] = imx_clk_hw_divider2("i2c4_pre_div", "i2c4_cg", base + 0xaf00, 16, 3); + hws[IMX7D_UART1_ROOT_PRE_DIV] = imx_clk_hw_divider2("uart1_pre_div", "uart1_cg", base + 0xaf80, 16, 3); + hws[IMX7D_UART2_ROOT_PRE_DIV] = imx_clk_hw_divider2("uart2_pre_div", "uart2_cg", base + 0xb000, 16, 3); + hws[IMX7D_UART3_ROOT_PRE_DIV] = imx_clk_hw_divider2("uart3_pre_div", "uart3_cg", base + 0xb080, 16, 3); + hws[IMX7D_UART4_ROOT_PRE_DIV] = imx_clk_hw_divider2("uart4_pre_div", "uart4_cg", base + 0xb100, 16, 3); + hws[IMX7D_UART5_ROOT_PRE_DIV] = imx_clk_hw_divider2("uart5_pre_div", "uart5_cg", base + 0xb180, 16, 3); + hws[IMX7D_UART6_ROOT_PRE_DIV] = imx_clk_hw_divider2("uart6_pre_div", "uart6_cg", base + 0xb200, 16, 3); + hws[IMX7D_UART7_ROOT_PRE_DIV] = imx_clk_hw_divider2("uart7_pre_div", "uart7_cg", base + 0xb280, 16, 3); + hws[IMX7D_ECSPI1_ROOT_PRE_DIV] = imx_clk_hw_divider2("ecspi1_pre_div", "ecspi1_cg", base + 0xb300, 16, 3); + hws[IMX7D_ECSPI2_ROOT_PRE_DIV] = imx_clk_hw_divider2("ecspi2_pre_div", "ecspi2_cg", base + 0xb380, 16, 3); + hws[IMX7D_ECSPI3_ROOT_PRE_DIV] = imx_clk_hw_divider2("ecspi3_pre_div", "ecspi3_cg", base + 0xb400, 16, 3); + hws[IMX7D_ECSPI4_ROOT_PRE_DIV] = imx_clk_hw_divider2("ecspi4_pre_div", "ecspi4_cg", base + 0xb480, 16, 3); + hws[IMX7D_PWM1_ROOT_PRE_DIV] = imx_clk_hw_divider2("pwm1_pre_div", "pwm1_cg", base + 0xb500, 16, 3); + hws[IMX7D_PWM2_ROOT_PRE_DIV] = imx_clk_hw_divider2("pwm2_pre_div", "pwm2_cg", base + 0xb580, 16, 3); + hws[IMX7D_PWM3_ROOT_PRE_DIV] = imx_clk_hw_divider2("pwm3_pre_div", "pwm3_cg", base + 0xb600, 16, 3); + hws[IMX7D_PWM4_ROOT_PRE_DIV] = imx_clk_hw_divider2("pwm4_pre_div", "pwm4_cg", base + 0xb680, 16, 3); + hws[IMX7D_FLEXTIMER1_ROOT_PRE_DIV] = imx_clk_hw_divider2("flextimer1_pre_div", "flextimer1_cg", base + 0xb700, 16, 3); + hws[IMX7D_FLEXTIMER2_ROOT_PRE_DIV] = imx_clk_hw_divider2("flextimer2_pre_div", "flextimer2_cg", base + 0xb780, 16, 3); + hws[IMX7D_SIM1_ROOT_PRE_DIV] = imx_clk_hw_divider2("sim1_pre_div", "sim1_cg", base + 0xb800, 16, 3); + hws[IMX7D_SIM2_ROOT_PRE_DIV] = imx_clk_hw_divider2("sim2_pre_div", "sim2_cg", base + 0xb880, 16, 3); + hws[IMX7D_GPT1_ROOT_PRE_DIV] = imx_clk_hw_divider2("gpt1_pre_div", "gpt1_cg", base + 0xb900, 16, 3); + hws[IMX7D_GPT2_ROOT_PRE_DIV] = imx_clk_hw_divider2("gpt2_pre_div", "gpt2_cg", base + 0xb980, 16, 3); + hws[IMX7D_GPT3_ROOT_PRE_DIV] = imx_clk_hw_divider2("gpt3_pre_div", "gpt3_cg", base + 0xba00, 16, 3); + hws[IMX7D_GPT4_ROOT_PRE_DIV] = imx_clk_hw_divider2("gpt4_pre_div", "gpt4_cg", base + 0xba80, 16, 3); + hws[IMX7D_TRACE_ROOT_PRE_DIV] = imx_clk_hw_divider2("trace_pre_div", "trace_cg", base + 0xbb00, 16, 3); + hws[IMX7D_WDOG_ROOT_PRE_DIV] = imx_clk_hw_divider2("wdog_pre_div", "wdog_cg", base + 0xbb80, 16, 3); + hws[IMX7D_CSI_MCLK_ROOT_PRE_DIV] = imx_clk_hw_divider2("csi_mclk_pre_div", "csi_mclk_cg", base + 0xbc00, 16, 3); + hws[IMX7D_AUDIO_MCLK_ROOT_PRE_DIV] = imx_clk_hw_divider2("audio_mclk_pre_div", "audio_mclk_cg", base + 0xbc80, 16, 3); + hws[IMX7D_WRCLK_ROOT_PRE_DIV] = imx_clk_hw_divider2("wrclk_pre_div", "wrclk_cg", base + 0xbd00, 16, 3); + hws[IMX7D_CLKO1_ROOT_PRE_DIV] = imx_clk_hw_divider2("clko1_pre_div", "clko1_cg", base + 0xbd80, 16, 3); + hws[IMX7D_CLKO2_ROOT_PRE_DIV] = imx_clk_hw_divider2("clko2_pre_div", "clko2_cg", base + 0xbe00, 16, 3); + + hws[IMX7D_ARM_A7_ROOT_DIV] = imx_clk_hw_divider2("arm_a7_div", "arm_a7_cg", base + 0x8000, 0, 3); + hws[IMX7D_ARM_M4_ROOT_DIV] = imx_clk_hw_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3); + hws[IMX7D_MAIN_AXI_ROOT_DIV] = imx_clk_hw_divider2("axi_post_div", "axi_pre_div", base + 0x8800, 0, 6); + hws[IMX7D_DISP_AXI_ROOT_DIV] = imx_clk_hw_divider2("disp_axi_post_div", "disp_axi_pre_div", base + 0x8880, 0, 6); + hws[IMX7D_ENET_AXI_ROOT_DIV] = imx_clk_hw_divider2("enet_axi_post_div", "enet_axi_pre_div", base + 0x8900, 0, 6); + hws[IMX7D_NAND_USDHC_BUS_ROOT_CLK] = imx_clk_hw_divider2("nand_usdhc_root_clk", "nand_usdhc_pre_div", base + 0x8980, 0, 6); + hws[IMX7D_AHB_CHANNEL_ROOT_DIV] = imx_clk_hw_divider2("ahb_root_clk", "ahb_pre_div", base + 0x9000, 0, 6); + hws[IMX7D_IPG_ROOT_CLK] = imx_clk_hw_divider_flags("ipg_root_clk", "ahb_root_clk", base + 0x9080, 0, 2, CLK_IS_CRITICAL | CLK_OPS_PARENT_ENABLE | CLK_SET_RATE_PARENT); + hws[IMX7D_DRAM_ROOT_DIV] = imx_clk_hw_divider2("dram_post_div", "dram_cg", base + 0x9880, 0, 3); + hws[IMX7D_DRAM_PHYM_ALT_ROOT_DIV] = imx_clk_hw_divider2("dram_phym_alt_post_div", "dram_phym_alt_pre_div", base + 0xa000, 0, 3); + hws[IMX7D_DRAM_ALT_ROOT_DIV] = imx_clk_hw_divider2("dram_alt_post_div", "dram_alt_pre_div", base + 0xa080, 0, 3); + hws[IMX7D_USB_HSIC_ROOT_DIV] = imx_clk_hw_divider2("usb_hsic_post_div", "usb_hsic_pre_div", base + 0xa100, 0, 6); + hws[IMX7D_PCIE_CTRL_ROOT_DIV] = imx_clk_hw_divider2("pcie_ctrl_post_div", "pcie_ctrl_pre_div", base + 0xa180, 0, 6); + hws[IMX7D_PCIE_PHY_ROOT_DIV] = imx_clk_hw_divider2("pcie_phy_post_div", "pcie_phy_pre_div", base + 0xa200, 0, 6); + hws[IMX7D_EPDC_PIXEL_ROOT_DIV] = imx_clk_hw_divider2("epdc_pixel_post_div", "epdc_pixel_pre_div", base + 0xa280, 0, 6); + hws[IMX7D_LCDIF_PIXEL_ROOT_DIV] = imx_clk_hw_divider2("lcdif_pixel_post_div", "lcdif_pixel_pre_div", base + 0xa300, 0, 6); + hws[IMX7D_MIPI_DSI_ROOT_DIV] = imx_clk_hw_divider2("mipi_dsi_post_div", "mipi_dsi_pre_div", base + 0xa380, 0, 6); + hws[IMX7D_MIPI_CSI_ROOT_DIV] = imx_clk_hw_divider2("mipi_csi_post_div", "mipi_csi_pre_div", base + 0xa400, 0, 6); + hws[IMX7D_MIPI_DPHY_ROOT_DIV] = imx_clk_hw_divider2("mipi_dphy_post_div", "mipi_dphy_pre_div", base + 0xa480, 0, 6); + hws[IMX7D_SAI1_ROOT_DIV] = imx_clk_hw_divider2("sai1_post_div", "sai1_pre_div", base + 0xa500, 0, 6); + hws[IMX7D_SAI2_ROOT_DIV] = imx_clk_hw_divider2("sai2_post_div", "sai2_pre_div", base + 0xa580, 0, 6); + hws[IMX7D_SAI3_ROOT_DIV] = imx_clk_hw_divider2("sai3_post_div", "sai3_pre_div", base + 0xa600, 0, 6); + hws[IMX7D_SPDIF_ROOT_DIV] = imx_clk_hw_divider2("spdif_post_div", "spdif_pre_div", base + 0xa680, 0, 6); + hws[IMX7D_ENET1_REF_ROOT_DIV] = imx_clk_hw_divider2("enet1_ref_post_div", "enet1_ref_pre_div", base + 0xa700, 0, 6); + hws[IMX7D_ENET1_TIME_ROOT_DIV] = imx_clk_hw_divider2("enet1_time_post_div", "enet1_time_pre_div", base + 0xa780, 0, 6); + hws[IMX7D_ENET2_REF_ROOT_DIV] = imx_clk_hw_divider2("enet2_ref_post_div", "enet2_ref_pre_div", base + 0xa800, 0, 6); + hws[IMX7D_ENET2_TIME_ROOT_DIV] = imx_clk_hw_divider2("enet2_time_post_div", "enet2_time_pre_div", base + 0xa880, 0, 6); + hws[IMX7D_ENET_PHY_REF_ROOT_CLK] = imx_clk_hw_divider2("enet_phy_ref_root_clk", "enet_phy_ref_pre_div", base + 0xa900, 0, 6); + hws[IMX7D_EIM_ROOT_DIV] = imx_clk_hw_divider2("eim_post_div", "eim_pre_div", base + 0xa980, 0, 6); + hws[IMX7D_NAND_ROOT_CLK] = imx_clk_hw_divider2("nand_root_clk", "nand_pre_div", base + 0xaa00, 0, 6); + hws[IMX7D_QSPI_ROOT_DIV] = imx_clk_hw_divider2("qspi_post_div", "qspi_pre_div", base + 0xaa80, 0, 6); + hws[IMX7D_USDHC1_ROOT_DIV] = imx_clk_hw_divider2("usdhc1_post_div", "usdhc1_pre_div", base + 0xab00, 0, 6); + hws[IMX7D_USDHC2_ROOT_DIV] = imx_clk_hw_divider2("usdhc2_post_div", "usdhc2_pre_div", base + 0xab80, 0, 6); + hws[IMX7D_USDHC3_ROOT_DIV] = imx_clk_hw_divider2("usdhc3_post_div", "usdhc3_pre_div", base + 0xac00, 0, 6); + hws[IMX7D_CAN1_ROOT_DIV] = imx_clk_hw_divider2("can1_post_div", "can1_pre_div", base + 0xac80, 0, 6); + hws[IMX7D_CAN2_ROOT_DIV] = imx_clk_hw_divider2("can2_post_div", "can2_pre_div", base + 0xad00, 0, 6); + hws[IMX7D_I2C1_ROOT_DIV] = imx_clk_hw_divider2("i2c1_post_div", "i2c1_pre_div", base + 0xad80, 0, 6); + hws[IMX7D_I2C2_ROOT_DIV] = imx_clk_hw_divider2("i2c2_post_div", "i2c2_pre_div", base + 0xae00, 0, 6); + hws[IMX7D_I2C3_ROOT_DIV] = imx_clk_hw_divider2("i2c3_post_div", "i2c3_pre_div", base + 0xae80, 0, 6); + hws[IMX7D_I2C4_ROOT_DIV] = imx_clk_hw_divider2("i2c4_post_div", "i2c4_pre_div", base + 0xaf00, 0, 6); + hws[IMX7D_UART1_ROOT_DIV] = imx_clk_hw_divider2("uart1_post_div", "uart1_pre_div", base + 0xaf80, 0, 6); + hws[IMX7D_UART2_ROOT_DIV] = imx_clk_hw_divider2("uart2_post_div", "uart2_pre_div", base + 0xb000, 0, 6); + hws[IMX7D_UART3_ROOT_DIV] = imx_clk_hw_divider2("uart3_post_div", "uart3_pre_div", base + 0xb080, 0, 6); + hws[IMX7D_UART4_ROOT_DIV] = imx_clk_hw_divider2("uart4_post_div", "uart4_pre_div", base + 0xb100, 0, 6); + hws[IMX7D_UART5_ROOT_DIV] = imx_clk_hw_divider2("uart5_post_div", "uart5_pre_div", base + 0xb180, 0, 6); + hws[IMX7D_UART6_ROOT_DIV] = imx_clk_hw_divider2("uart6_post_div", "uart6_pre_div", base + 0xb200, 0, 6); + hws[IMX7D_UART7_ROOT_DIV] = imx_clk_hw_divider2("uart7_post_div", "uart7_pre_div", base + 0xb280, 0, 6); + hws[IMX7D_ECSPI1_ROOT_DIV] = imx_clk_hw_divider2("ecspi1_post_div", "ecspi1_pre_div", base + 0xb300, 0, 6); + hws[IMX7D_ECSPI2_ROOT_DIV] = imx_clk_hw_divider2("ecspi2_post_div", "ecspi2_pre_div", base + 0xb380, 0, 6); + hws[IMX7D_ECSPI3_ROOT_DIV] = imx_clk_hw_divider2("ecspi3_post_div", "ecspi3_pre_div", base + 0xb400, 0, 6); + hws[IMX7D_ECSPI4_ROOT_DIV] = imx_clk_hw_divider2("ecspi4_post_div", "ecspi4_pre_div", base + 0xb480, 0, 6); + hws[IMX7D_PWM1_ROOT_DIV] = imx_clk_hw_divider2("pwm1_post_div", "pwm1_pre_div", base + 0xb500, 0, 6); + hws[IMX7D_PWM2_ROOT_DIV] = imx_clk_hw_divider2("pwm2_post_div", "pwm2_pre_div", base + 0xb580, 0, 6); + hws[IMX7D_PWM3_ROOT_DIV] = imx_clk_hw_divider2("pwm3_post_div", "pwm3_pre_div", base + 0xb600, 0, 6); + hws[IMX7D_PWM4_ROOT_DIV] = imx_clk_hw_divider2("pwm4_post_div", "pwm4_pre_div", base + 0xb680, 0, 6); + hws[IMX7D_FLEXTIMER1_ROOT_DIV] = imx_clk_hw_divider2("flextimer1_post_div", "flextimer1_pre_div", base + 0xb700, 0, 6); + hws[IMX7D_FLEXTIMER2_ROOT_DIV] = imx_clk_hw_divider2("flextimer2_post_div", "flextimer2_pre_div", base + 0xb780, 0, 6); + hws[IMX7D_SIM1_ROOT_DIV] = imx_clk_hw_divider2("sim1_post_div", "sim1_pre_div", base + 0xb800, 0, 6); + hws[IMX7D_SIM2_ROOT_DIV] = imx_clk_hw_divider2("sim2_post_div", "sim2_pre_div", base + 0xb880, 0, 6); + hws[IMX7D_GPT1_ROOT_DIV] = imx_clk_hw_divider2("gpt1_post_div", "gpt1_pre_div", base + 0xb900, 0, 6); + hws[IMX7D_GPT2_ROOT_DIV] = imx_clk_hw_divider2("gpt2_post_div", "gpt2_pre_div", base + 0xb980, 0, 6); + hws[IMX7D_GPT3_ROOT_DIV] = imx_clk_hw_divider2("gpt3_post_div", "gpt3_pre_div", base + 0xba00, 0, 6); + hws[IMX7D_GPT4_ROOT_DIV] = imx_clk_hw_divider2("gpt4_post_div", "gpt4_pre_div", base + 0xba80, 0, 6); + hws[IMX7D_TRACE_ROOT_DIV] = imx_clk_hw_divider2("trace_post_div", "trace_pre_div", base + 0xbb00, 0, 6); + hws[IMX7D_WDOG_ROOT_DIV] = imx_clk_hw_divider2("wdog_post_div", "wdog_pre_div", base + 0xbb80, 0, 6); + hws[IMX7D_CSI_MCLK_ROOT_DIV] = imx_clk_hw_divider2("csi_mclk_post_div", "csi_mclk_pre_div", base + 0xbc00, 0, 6); + hws[IMX7D_AUDIO_MCLK_ROOT_DIV] = imx_clk_hw_divider2("audio_mclk_post_div", "audio_mclk_pre_div", base + 0xbc80, 0, 6); + hws[IMX7D_WRCLK_ROOT_DIV] = imx_clk_hw_divider2("wrclk_post_div", "wrclk_pre_div", base + 0xbd00, 0, 6); + hws[IMX7D_CLKO1_ROOT_DIV] = imx_clk_hw_divider2("clko1_post_div", "clko1_pre_div", base + 0xbd80, 0, 6); + hws[IMX7D_CLKO2_ROOT_DIV] = imx_clk_hw_divider2("clko2_post_div", "clko2_pre_div", base + 0xbe00, 0, 6); + + hws[IMX7D_ARM_A7_ROOT_CLK] = imx_clk_hw_gate2_flags("arm_a7_root_clk", "arm_a7_div", base + 0x4000, 0, CLK_OPS_PARENT_ENABLE); + hws[IMX7D_ARM_M4_ROOT_CLK] = imx_clk_hw_gate4("arm_m4_root_clk", "arm_m4_div", base + 0x4010, 0); + hws[IMX7D_MAIN_AXI_ROOT_CLK] = imx_clk_hw_gate2_flags("main_axi_root_clk", "axi_post_div", base + 0x4040, 0, CLK_IS_CRITICAL | CLK_OPS_PARENT_ENABLE); + hws[IMX7D_DISP_AXI_ROOT_CLK] = imx_clk_hw_gate4("disp_axi_root_clk", "disp_axi_post_div", base + 0x4050, 0); + hws[IMX7D_ENET_AXI_ROOT_CLK] = imx_clk_hw_gate4("enet_axi_root_clk", "enet_axi_post_div", base + 0x4060, 0); + hws[IMX7D_OCRAM_CLK] = imx_clk_hw_gate4("ocram_clk", "main_axi_root_clk", base + 0x4110, 0); + hws[IMX7D_OCRAM_S_CLK] = imx_clk_hw_gate4("ocram_s_clk", "ahb_root_clk", base + 0x4120, 0); + hws[IMX7D_DRAM_ROOT_CLK] = imx_clk_hw_gate2_flags("dram_root_clk", "dram_post_div", base + 0x4130, 0, CLK_IS_CRITICAL | CLK_OPS_PARENT_ENABLE); + hws[IMX7D_DRAM_PHYM_ROOT_CLK] = imx_clk_hw_gate2_flags("dram_phym_root_clk", "dram_phym_cg", base + 0x4130, 0, CLK_IS_CRITICAL | CLK_OPS_PARENT_ENABLE); + hws[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_hw_gate2_flags("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0, CLK_IS_CRITICAL | CLK_OPS_PARENT_ENABLE); + hws[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_hw_gate2_flags("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0, CLK_IS_CRITICAL | CLK_OPS_PARENT_ENABLE); + hws[IMX7D_OCOTP_CLK] = imx_clk_hw_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0); + hws[IMX7D_MU_ROOT_CLK] = imx_clk_hw_gate4("mu_root_clk", "ipg_root_clk", base + 0x4270, 0); + hws[IMX7D_CAAM_CLK] = imx_clk_hw_gate4("caam_clk", "ipg_root_clk", base + 0x4240, 0); + hws[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_hw_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4690, 0); + hws[IMX7D_SDMA_CORE_CLK] = imx_clk_hw_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0); + hws[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_hw_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0); + hws[IMX7D_PCIE_PHY_ROOT_CLK] = imx_clk_hw_gate4("pcie_phy_root_clk", "pcie_phy_post_div", base + 0x4600, 0); + hws[IMX7D_EPDC_PIXEL_ROOT_CLK] = imx_clk_hw_gate4("epdc_pixel_root_clk", "epdc_pixel_post_div", base + 0x44a0, 0); + hws[IMX7D_LCDIF_PIXEL_ROOT_CLK] = imx_clk_hw_gate4("lcdif_pixel_root_clk", "lcdif_pixel_post_div", base + 0x44b0, 0); + hws[IMX7D_PXP_CLK] = imx_clk_hw_gate4("pxp_clk", "main_axi_root_clk", base + 0x44c0, 0); + hws[IMX7D_MIPI_DSI_ROOT_CLK] = imx_clk_hw_gate4("mipi_dsi_root_clk", "mipi_dsi_post_div", base + 0x4650, 0); + hws[IMX7D_MIPI_CSI_ROOT_CLK] = imx_clk_hw_gate4("mipi_csi_root_clk", "mipi_csi_post_div", base + 0x4640, 0); + hws[IMX7D_MIPI_DPHY_ROOT_CLK] = imx_clk_hw_gate4("mipi_dphy_root_clk", "mipi_dphy_post_div", base + 0x4660, 0); + hws[IMX7D_ENET1_IPG_ROOT_CLK] = imx_clk_hw_gate2_shared2("enet1_ipg_root_clk", "enet_axi_post_div", base + 0x4700, 0, &share_count_enet1); + hws[IMX7D_ENET1_TIME_ROOT_CLK] = imx_clk_hw_gate2_shared2("enet1_time_root_clk", "enet1_time_post_div", base + 0x4700, 0, &share_count_enet1); + hws[IMX7D_ENET2_IPG_ROOT_CLK] = imx_clk_hw_gate2_shared2("enet2_ipg_root_clk", "enet_axi_post_div", base + 0x4710, 0, &share_count_enet2); + hws[IMX7D_ENET2_TIME_ROOT_CLK] = imx_clk_hw_gate2_shared2("enet2_time_root_clk", "enet2_time_post_div", base + 0x4710, 0, &share_count_enet2); + hws[IMX7D_SAI1_ROOT_CLK] = imx_clk_hw_gate2_shared2("sai1_root_clk", "sai1_post_div", base + 0x48c0, 0, &share_count_sai1); + hws[IMX7D_SAI1_IPG_CLK] = imx_clk_hw_gate2_shared2("sai1_ipg_clk", "ipg_root_clk", base + 0x48c0, 0, &share_count_sai1); + hws[IMX7D_SAI2_ROOT_CLK] = imx_clk_hw_gate2_shared2("sai2_root_clk", "sai2_post_div", base + 0x48d0, 0, &share_count_sai2); + hws[IMX7D_SAI2_IPG_CLK] = imx_clk_hw_gate2_shared2("sai2_ipg_clk", "ipg_root_clk", base + 0x48d0, 0, &share_count_sai2); + hws[IMX7D_SAI3_ROOT_CLK] = imx_clk_hw_gate2_shared2("sai3_root_clk", "sai3_post_div", base + 0x48e0, 0, &share_count_sai3); + hws[IMX7D_SAI3_IPG_CLK] = imx_clk_hw_gate2_shared2("sai3_ipg_clk", "ipg_root_clk", base + 0x48e0, 0, &share_count_sai3); + hws[IMX7D_SPDIF_ROOT_CLK] = imx_clk_hw_gate4("spdif_root_clk", "spdif_post_div", base + 0x44d0, 0); + hws[IMX7D_EIM_ROOT_CLK] = imx_clk_hw_gate4("eim_root_clk", "eim_post_div", base + 0x4160, 0); + hws[IMX7D_NAND_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_rawnand_clk", "nand_root_clk", base + 0x4140, 0, &share_count_nand); + hws[IMX7D_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_root_clk", base + 0x4140, 0, &share_count_nand); + hws[IMX7D_QSPI_ROOT_CLK] = imx_clk_hw_gate4("qspi_root_clk", "qspi_post_div", base + 0x4150, 0); + hws[IMX7D_USDHC1_ROOT_CLK] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1_post_div", base + 0x46c0, 0); + hws[IMX7D_USDHC2_ROOT_CLK] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2_post_div", base + 0x46d0, 0); + hws[IMX7D_USDHC3_ROOT_CLK] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3_post_div", base + 0x46e0, 0); + hws[IMX7D_CAN1_ROOT_CLK] = imx_clk_hw_gate4("can1_root_clk", "can1_post_div", base + 0x4740, 0); + hws[IMX7D_CAN2_ROOT_CLK] = imx_clk_hw_gate4("can2_root_clk", "can2_post_div", base + 0x4750, 0); + hws[IMX7D_I2C1_ROOT_CLK] = imx_clk_hw_gate4("i2c1_root_clk", "i2c1_post_div", base + 0x4880, 0); + hws[IMX7D_I2C2_ROOT_CLK] = imx_clk_hw_gate4("i2c2_root_clk", "i2c2_post_div", base + 0x4890, 0); + hws[IMX7D_I2C3_ROOT_CLK] = imx_clk_hw_gate4("i2c3_root_clk", "i2c3_post_div", base + 0x48a0, 0); + hws[IMX7D_I2C4_ROOT_CLK] = imx_clk_hw_gate4("i2c4_root_clk", "i2c4_post_div", base + 0x48b0, 0); + hws[IMX7D_UART1_ROOT_CLK] = imx_clk_hw_gate4("uart1_root_clk", "uart1_post_div", base + 0x4940, 0); + hws[IMX7D_UART2_ROOT_CLK] = imx_clk_hw_gate4("uart2_root_clk", "uart2_post_div", base + 0x4950, 0); + hws[IMX7D_UART3_ROOT_CLK] = imx_clk_hw_gate4("uart3_root_clk", "uart3_post_div", base + 0x4960, 0); + hws[IMX7D_UART4_ROOT_CLK] = imx_clk_hw_gate4("uart4_root_clk", "uart4_post_div", base + 0x4970, 0); + hws[IMX7D_UART5_ROOT_CLK] = imx_clk_hw_gate4("uart5_root_clk", "uart5_post_div", base + 0x4980, 0); + hws[IMX7D_UART6_ROOT_CLK] = imx_clk_hw_gate4("uart6_root_clk", "uart6_post_div", base + 0x4990, 0); + hws[IMX7D_UART7_ROOT_CLK] = imx_clk_hw_gate4("uart7_root_clk", "uart7_post_div", base + 0x49a0, 0); + hws[IMX7D_ECSPI1_ROOT_CLK] = imx_clk_hw_gate4("ecspi1_root_clk", "ecspi1_post_div", base + 0x4780, 0); + hws[IMX7D_ECSPI2_ROOT_CLK] = imx_clk_hw_gate4("ecspi2_root_clk", "ecspi2_post_div", base + 0x4790, 0); + hws[IMX7D_ECSPI3_ROOT_CLK] = imx_clk_hw_gate4("ecspi3_root_clk", "ecspi3_post_div", base + 0x47a0, 0); + hws[IMX7D_ECSPI4_ROOT_CLK] = imx_clk_hw_gate4("ecspi4_root_clk", "ecspi4_post_div", base + 0x47b0, 0); + hws[IMX7D_PWM1_ROOT_CLK] = imx_clk_hw_gate4("pwm1_root_clk", "pwm1_post_div", base + 0x4840, 0); + hws[IMX7D_PWM2_ROOT_CLK] = imx_clk_hw_gate4("pwm2_root_clk", "pwm2_post_div", base + 0x4850, 0); + hws[IMX7D_PWM3_ROOT_CLK] = imx_clk_hw_gate4("pwm3_root_clk", "pwm3_post_div", base + 0x4860, 0); + hws[IMX7D_PWM4_ROOT_CLK] = imx_clk_hw_gate4("pwm4_root_clk", "pwm4_post_div", base + 0x4870, 0); + hws[IMX7D_FLEXTIMER1_ROOT_CLK] = imx_clk_hw_gate4("flextimer1_root_clk", "flextimer1_post_div", base + 0x4800, 0); + hws[IMX7D_FLEXTIMER2_ROOT_CLK] = imx_clk_hw_gate4("flextimer2_root_clk", "flextimer2_post_div", base + 0x4810, 0); + hws[IMX7D_SIM1_ROOT_CLK] = imx_clk_hw_gate4("sim1_root_clk", "sim1_post_div", base + 0x4900, 0); + hws[IMX7D_SIM2_ROOT_CLK] = imx_clk_hw_gate4("sim2_root_clk", "sim2_post_div", base + 0x4910, 0); + hws[IMX7D_GPT1_ROOT_CLK] = imx_clk_hw_gate4("gpt1_root_clk", "gpt1_post_div", base + 0x47c0, 0); + hws[IMX7D_GPT2_ROOT_CLK] = imx_clk_hw_gate4("gpt2_root_clk", "gpt2_post_div", base + 0x47d0, 0); + hws[IMX7D_GPT3_ROOT_CLK] = imx_clk_hw_gate4("gpt3_root_clk", "gpt3_post_div", base + 0x47e0, 0); + hws[IMX7D_GPT4_ROOT_CLK] = imx_clk_hw_gate4("gpt4_root_clk", "gpt4_post_div", base + 0x47f0, 0); + hws[IMX7D_TRACE_ROOT_CLK] = imx_clk_hw_gate4("trace_root_clk", "trace_post_div", base + 0x4300, 0); + hws[IMX7D_WDOG1_ROOT_CLK] = imx_clk_hw_gate4("wdog1_root_clk", "wdog_post_div", base + 0x49c0, 0); + hws[IMX7D_WDOG2_ROOT_CLK] = imx_clk_hw_gate4("wdog2_root_clk", "wdog_post_div", base + 0x49d0, 0); + hws[IMX7D_WDOG3_ROOT_CLK] = imx_clk_hw_gate4("wdog3_root_clk", "wdog_post_div", base + 0x49e0, 0); + hws[IMX7D_WDOG4_ROOT_CLK] = imx_clk_hw_gate4("wdog4_root_clk", "wdog_post_div", base + 0x49f0, 0); + hws[IMX7D_KPP_ROOT_CLK] = imx_clk_hw_gate4("kpp_root_clk", "ipg_root_clk", base + 0x4aa0, 0); + hws[IMX7D_CSI_MCLK_ROOT_CLK] = imx_clk_hw_gate4("csi_mclk_root_clk", "csi_mclk_post_div", base + 0x4490, 0); + hws[IMX7D_WRCLK_ROOT_CLK] = imx_clk_hw_gate4("wrclk_root_clk", "wrclk_post_div", base + 0x47a0, 0); + hws[IMX7D_USB_CTRL_CLK] = imx_clk_hw_gate4("usb_ctrl_clk", "ahb_root_clk", base + 0x4680, 0); + hws[IMX7D_USB_PHY1_CLK] = imx_clk_hw_gate4("usb_phy1_clk", "pll_usb1_main_clk", base + 0x46a0, 0); + hws[IMX7D_USB_PHY2_CLK] = imx_clk_hw_gate4("usb_phy2_clk", "pll_usb_main_clk", base + 0x46b0, 0); + hws[IMX7D_ADC_ROOT_CLK] = imx_clk_hw_gate4("adc_root_clk", "ipg_root_clk", base + 0x4200, 0); + + hws[IMX7D_GPT_3M_CLK] = imx_clk_hw_fixed_factor("gpt_3m", "osc", 1, 8); + + hws[IMX7D_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a7_root_clk", + hws[IMX7D_ARM_A7_ROOT_CLK]->clk, + hws[IMX7D_ARM_A7_ROOT_SRC]->clk, + hws[IMX7D_PLL_ARM_MAIN_CLK]->clk, + hws[IMX7D_PLL_SYS_MAIN_CLK]->clk); + + imx_check_clk_hws(hws, IMX7D_CLK_END); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + + clk_set_parent(hws[IMX7D_PLL_ARM_MAIN_BYPASS]->clk, hws[IMX7D_PLL_ARM_MAIN]->clk); + clk_set_parent(hws[IMX7D_PLL_DRAM_MAIN_BYPASS]->clk, hws[IMX7D_PLL_DRAM_MAIN]->clk); + clk_set_parent(hws[IMX7D_PLL_SYS_MAIN_BYPASS]->clk, hws[IMX7D_PLL_SYS_MAIN]->clk); + clk_set_parent(hws[IMX7D_PLL_ENET_MAIN_BYPASS]->clk, hws[IMX7D_PLL_ENET_MAIN]->clk); + clk_set_parent(hws[IMX7D_PLL_AUDIO_MAIN_BYPASS]->clk, hws[IMX7D_PLL_AUDIO_MAIN]->clk); + clk_set_parent(hws[IMX7D_PLL_VIDEO_MAIN_BYPASS]->clk, hws[IMX7D_PLL_VIDEO_MAIN]->clk); + + clk_set_parent(hws[IMX7D_MIPI_CSI_ROOT_SRC]->clk, hws[IMX7D_PLL_SYS_PFD3_CLK]->clk); + + /* use old gpt clk setting, gpt1 root clk must be twice as gpt counter freq */ + clk_set_parent(hws[IMX7D_GPT1_ROOT_SRC]->clk, hws[IMX7D_OSC_24M_CLK]->clk); + + /* Set clock rate for USBPHY, the USB_PLL at CCM is from USBOTG2 */ + hws[IMX7D_USB1_MAIN_480M_CLK] = imx_clk_hw_fixed_factor("pll_usb1_main_clk", "osc", 20, 1); + hws[IMX7D_USB_MAIN_480M_CLK] = imx_clk_hw_fixed_factor("pll_usb_main_clk", "osc", 20, 1); + + imx_register_uart_clocks(7); + +} +CLK_OF_DECLARE(imx7d, "fsl,imx7d-ccm", imx7d_clocks_init); diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c new file mode 100644 index 000000000..b6e45e77e --- /dev/null +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Author: Dong Aisheng <aisheng.dong@nxp.com> + * + */ + +#include <dt-bindings/clock/imx7ulp-clock.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "clk.h" + +static const char * const pll_pre_sels[] = { "sosc", "firc", }; +static const char * const spll_pfd_sels[] = { "spll_pfd0", "spll_pfd1", "spll_pfd2", "spll_pfd3", }; +static const char * const spll_sels[] = { "spll", "spll_pfd_sel", }; +static const char * const apll_pfd_sels[] = { "apll_pfd0", "apll_pfd1", "apll_pfd2", "apll_pfd3", }; +static const char * const apll_sels[] = { "apll", "apll_pfd_sel", }; +static const char * const scs_sels[] = { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "dummy", }; +static const char * const ddr_sels[] = { "apll_pfd_sel", "dummy", "dummy", "dummy", }; +static const char * const nic_sels[] = { "firc", "ddr_clk", }; +static const char * const periph_plat_sels[] = { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", }; +static const char * const periph_bus_sels[] = { "dummy", "sosc_bus_clk", "dummy", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", }; +static const char * const arm_sels[] = { "core", "dummy", "dummy", "hsrun_core", }; + +/* used by sosc/sirc/firc/ddr/spll/apll dividers */ +static const struct clk_div_table ulp_div_table[] = { + { .val = 1, .div = 1, }, + { .val = 2, .div = 2, }, + { .val = 3, .div = 4, }, + { .val = 4, .div = 8, }, + { .val = 5, .div = 16, }, + { .val = 6, .div = 32, }, + { .val = 7, .div = 64, }, + { /* sentinel */ }, +}; + +static void __init imx7ulp_clk_scg1_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct clk_hw **hws; + void __iomem *base; + + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SCG1_END), + GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = IMX7ULP_CLK_SCG1_END; + hws = clk_data->hws; + + hws[IMX7ULP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + hws[IMX7ULP_CLK_ROSC] = imx_obtain_fixed_clk_hw(np, "rosc"); + hws[IMX7ULP_CLK_SOSC] = imx_obtain_fixed_clk_hw(np, "sosc"); + hws[IMX7ULP_CLK_SIRC] = imx_obtain_fixed_clk_hw(np, "sirc"); + hws[IMX7ULP_CLK_FIRC] = imx_obtain_fixed_clk_hw(np, "firc"); + hws[IMX7ULP_CLK_UPLL] = imx_obtain_fixed_clk_hw(np, "upll"); + + /* SCG1 */ + base = of_iomap(np, 0); + WARN_ON(!base); + + /* NOTE: xPLL config can't be changed when xPLL is enabled */ + hws[IMX7ULP_CLK_APLL_PRE_SEL] = imx_clk_hw_mux_flags("apll_pre_sel", base + 0x508, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + hws[IMX7ULP_CLK_SPLL_PRE_SEL] = imx_clk_hw_mux_flags("spll_pre_sel", base + 0x608, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + + /* name parent_name reg shift width flags */ + hws[IMX7ULP_CLK_APLL_PRE_DIV] = imx_clk_hw_divider_flags("apll_pre_div", "apll_pre_sel", base + 0x508, 8, 3, CLK_SET_RATE_GATE); + hws[IMX7ULP_CLK_SPLL_PRE_DIV] = imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608, 8, 3, CLK_SET_RATE_GATE); + + /* name parent_name base */ + hws[IMX7ULP_CLK_APLL] = imx_clk_hw_pllv4(IMX_PLLV4_IMX7ULP, "apll", "apll_pre_div", base + 0x500); + hws[IMX7ULP_CLK_SPLL] = imx_clk_hw_pllv4(IMX_PLLV4_IMX7ULP, "spll", "spll_pre_div", base + 0x600); + + /* APLL PFDs */ + hws[IMX7ULP_CLK_APLL_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd0", "apll", base + 0x50c, 0); + hws[IMX7ULP_CLK_APLL_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd1", "apll", base + 0x50c, 1); + hws[IMX7ULP_CLK_APLL_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd2", "apll", base + 0x50c, 2); + hws[IMX7ULP_CLK_APLL_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd3", "apll", base + 0x50c, 3); + + /* SPLL PFDs */ + hws[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd0", "spll", base + 0x60C, 0); + hws[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd1", "spll", base + 0x60C, 1); + hws[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd2", "spll", base + 0x60C, 2); + hws[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd3", "spll", base + 0x60C, 3); + + /* PLL Mux */ + hws[IMX7ULP_CLK_APLL_PFD_SEL] = imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + hws[IMX7ULP_CLK_SPLL_PFD_SEL] = imx_clk_hw_mux_flags("spll_pfd_sel", base + 0x608, 14, 2, spll_pfd_sels, ARRAY_SIZE(spll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + hws[IMX7ULP_CLK_APLL_SEL] = imx_clk_hw_mux_flags("apll_sel", base + 0x508, 1, 1, apll_sels, ARRAY_SIZE(apll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + hws[IMX7ULP_CLK_SPLL_SEL] = imx_clk_hw_mux_flags("spll_sel", base + 0x608, 1, 1, spll_sels, ARRAY_SIZE(spll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + + hws[IMX7ULP_CLK_SPLL_BUS_CLK] = imx_clk_hw_divider_gate("spll_bus_clk", "spll_sel", CLK_SET_RATE_GATE, base + 0x604, 8, 3, 0, ulp_div_table, &imx_ccm_lock); + + /* scs/ddr/nic select different clock source requires that clock to be enabled first */ + hws[IMX7ULP_CLK_SYS_SEL] = imx_clk_hw_mux2("scs_sel", base + 0x14, 24, 4, scs_sels, ARRAY_SIZE(scs_sels)); + hws[IMX7ULP_CLK_HSRUN_SYS_SEL] = imx_clk_hw_mux2("hsrun_scs_sel", base + 0x1c, 24, 4, scs_sels, ARRAY_SIZE(scs_sels)); + hws[IMX7ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x40, 28, 1, nic_sels, ARRAY_SIZE(nic_sels)); + hws[IMX7ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 2, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); + + hws[IMX7ULP_CLK_CORE_DIV] = imx_clk_hw_divider_flags("divcore", "scs_sel", base + 0x14, 16, 4, CLK_SET_RATE_PARENT); + hws[IMX7ULP_CLK_CORE] = imx_clk_hw_cpu("core", "divcore", hws[IMX7ULP_CLK_CORE_DIV]->clk, hws[IMX7ULP_CLK_SYS_SEL]->clk, hws[IMX7ULP_CLK_SPLL_SEL]->clk, hws[IMX7ULP_CLK_FIRC]->clk); + hws[IMX7ULP_CLK_HSRUN_CORE_DIV] = imx_clk_hw_divider_flags("hsrun_divcore", "hsrun_scs_sel", base + 0x1c, 16, 4, CLK_SET_RATE_PARENT); + hws[IMX7ULP_CLK_HSRUN_CORE] = imx_clk_hw_cpu("hsrun_core", "hsrun_divcore", hws[IMX7ULP_CLK_HSRUN_CORE_DIV]->clk, hws[IMX7ULP_CLK_HSRUN_SYS_SEL]->clk, hws[IMX7ULP_CLK_SPLL_SEL]->clk, hws[IMX7ULP_CLK_FIRC]->clk); + + hws[IMX7ULP_CLK_DDR_DIV] = imx_clk_hw_divider_gate("ddr_clk", "ddr_sel", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, base + 0x30, 0, 3, + 0, ulp_div_table, &imx_ccm_lock); + + hws[IMX7ULP_CLK_NIC0_DIV] = imx_clk_hw_divider_flags("nic0_clk", "nic_sel", base + 0x40, 24, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + hws[IMX7ULP_CLK_NIC1_DIV] = imx_clk_hw_divider_flags("nic1_clk", "nic0_clk", base + 0x40, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + hws[IMX7ULP_CLK_NIC1_BUS_DIV] = imx_clk_hw_divider_flags("nic1_bus_clk", "nic0_clk", base + 0x40, 4, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + + hws[IMX7ULP_CLK_GPU_DIV] = imx_clk_hw_divider("gpu_clk", "nic0_clk", base + 0x40, 20, 4); + + hws[IMX7ULP_CLK_SOSC_BUS_CLK] = imx_clk_hw_divider_gate("sosc_bus_clk", "sosc", 0, base + 0x104, 8, 3, + CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock); + hws[IMX7ULP_CLK_FIRC_BUS_CLK] = imx_clk_hw_divider_gate("firc_bus_clk", "firc", 0, base + 0x304, 8, 3, + CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock); + + imx_check_clk_hws(hws, clk_data->num); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); +} +CLK_OF_DECLARE(imx7ulp_clk_scg1, "fsl,imx7ulp-scg1", imx7ulp_clk_scg1_init); + +static void __init imx7ulp_clk_pcc2_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct clk_hw **hws; + void __iomem *base; + + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC2_END), + GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = IMX7ULP_CLK_PCC2_END; + hws = clk_data->hws; + + /* PCC2 */ + base = of_iomap(np, 0); + WARN_ON(!base); + + hws[IMX7ULP_CLK_DMA1] = imx_clk_hw_gate("dma1", "nic1_clk", base + 0x20, 30); + hws[IMX7ULP_CLK_RGPIO2P1] = imx_clk_hw_gate("rgpio2p1", "nic1_bus_clk", base + 0x3c, 30); + hws[IMX7ULP_CLK_DMA_MUX1] = imx_clk_hw_gate("dma_mux1", "nic1_bus_clk", base + 0x84, 30); + hws[IMX7ULP_CLK_CAAM] = imx_clk_hw_gate("caam", "nic1_clk", base + 0x90, 30); + hws[IMX7ULP_CLK_LPTPM4] = imx7ulp_clk_hw_composite("lptpm4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94); + hws[IMX7ULP_CLK_LPTPM5] = imx7ulp_clk_hw_composite("lptpm5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98); + hws[IMX7ULP_CLK_LPIT1] = imx7ulp_clk_hw_composite("lpit1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c); + hws[IMX7ULP_CLK_LPSPI2] = imx7ulp_clk_hw_composite("lpspi2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa4); + hws[IMX7ULP_CLK_LPSPI3] = imx7ulp_clk_hw_composite("lpspi3", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa8); + hws[IMX7ULP_CLK_LPI2C4] = imx7ulp_clk_hw_composite("lpi2c4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xac); + hws[IMX7ULP_CLK_LPI2C5] = imx7ulp_clk_hw_composite("lpi2c5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb0); + hws[IMX7ULP_CLK_LPUART4] = imx7ulp_clk_hw_composite("lpuart4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb4); + hws[IMX7ULP_CLK_LPUART5] = imx7ulp_clk_hw_composite("lpuart5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb8); + hws[IMX7ULP_CLK_FLEXIO1] = imx7ulp_clk_hw_composite("flexio1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xc4); + hws[IMX7ULP_CLK_USB0] = imx7ulp_clk_hw_composite("usb0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xcc); + hws[IMX7ULP_CLK_USB1] = imx7ulp_clk_hw_composite("usb1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xd0); + hws[IMX7ULP_CLK_USB_PHY] = imx_clk_hw_gate("usb_phy", "nic1_bus_clk", base + 0xd4, 30); + hws[IMX7ULP_CLK_USDHC0] = imx7ulp_clk_hw_composite("usdhc0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xdc); + hws[IMX7ULP_CLK_USDHC1] = imx7ulp_clk_hw_composite("usdhc1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xe0); + hws[IMX7ULP_CLK_WDG1] = imx7ulp_clk_hw_composite("wdg1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xf4); + hws[IMX7ULP_CLK_WDG2] = imx7ulp_clk_hw_composite("wdg2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0x10c); + + imx_check_clk_hws(hws, clk_data->num); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); + + imx_register_uart_clocks(2); +} +CLK_OF_DECLARE(imx7ulp_clk_pcc2, "fsl,imx7ulp-pcc2", imx7ulp_clk_pcc2_init); + +static void __init imx7ulp_clk_pcc3_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct clk_hw **hws; + void __iomem *base; + + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_PCC3_END), + GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = IMX7ULP_CLK_PCC3_END; + hws = clk_data->hws; + + /* PCC3 */ + base = of_iomap(np, 0); + WARN_ON(!base); + + hws[IMX7ULP_CLK_LPTPM6] = imx7ulp_clk_hw_composite("lptpm6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x84); + hws[IMX7ULP_CLK_LPTPM7] = imx7ulp_clk_hw_composite("lptpm7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x88); + + hws[IMX7ULP_CLK_MMDC] = clk_hw_register_gate(NULL, "mmdc", "nic1_clk", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + base + 0xac, 30, 0, &imx_ccm_lock); + hws[IMX7ULP_CLK_LPI2C6] = imx7ulp_clk_hw_composite("lpi2c6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x90); + hws[IMX7ULP_CLK_LPI2C7] = imx7ulp_clk_hw_composite("lpi2c7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94); + hws[IMX7ULP_CLK_LPUART6] = imx7ulp_clk_hw_composite("lpuart6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98); + hws[IMX7ULP_CLK_LPUART7] = imx7ulp_clk_hw_composite("lpuart7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c); + hws[IMX7ULP_CLK_DSI] = imx7ulp_clk_hw_composite("dsi", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xa4); + hws[IMX7ULP_CLK_LCDIF] = imx7ulp_clk_hw_composite("lcdif", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xa8); + + hws[IMX7ULP_CLK_VIU] = imx_clk_hw_gate("viu", "nic1_clk", base + 0xa0, 30); + hws[IMX7ULP_CLK_PCTLC] = imx_clk_hw_gate("pctlc", "nic1_bus_clk", base + 0xb8, 30); + hws[IMX7ULP_CLK_PCTLD] = imx_clk_hw_gate("pctld", "nic1_bus_clk", base + 0xbc, 30); + hws[IMX7ULP_CLK_PCTLE] = imx_clk_hw_gate("pctle", "nic1_bus_clk", base + 0xc0, 30); + hws[IMX7ULP_CLK_PCTLF] = imx_clk_hw_gate("pctlf", "nic1_bus_clk", base + 0xc4, 30); + + hws[IMX7ULP_CLK_GPU3D] = imx7ulp_clk_hw_composite("gpu3d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x140); + hws[IMX7ULP_CLK_GPU2D] = imx7ulp_clk_hw_composite("gpu2d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x144); + + imx_check_clk_hws(hws, clk_data->num); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); + + imx_register_uart_clocks(7); +} +CLK_OF_DECLARE(imx7ulp_clk_pcc3, "fsl,imx7ulp-pcc3", imx7ulp_clk_pcc3_init); + +static void __init imx7ulp_clk_smc1_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct clk_hw **hws; + void __iomem *base; + + clk_data = kzalloc(struct_size(clk_data, hws, IMX7ULP_CLK_SMC1_END), + GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = IMX7ULP_CLK_SMC1_END; + hws = clk_data->hws; + + /* SMC1 */ + base = of_iomap(np, 0); + WARN_ON(!base); + + hws[IMX7ULP_CLK_ARM] = imx_clk_hw_mux_flags("arm", base + 0x10, 8, 2, arm_sels, ARRAY_SIZE(arm_sels), CLK_SET_RATE_PARENT); + + imx_check_clk_hws(hws, clk_data->num); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); +} +CLK_OF_DECLARE(imx7ulp_clk_smc1, "fsl,imx7ulp-smc1", imx7ulp_clk_smc1_init); diff --git a/drivers/clk/imx/clk-imx8dxl-rsrc.c b/drivers/clk/imx/clk-imx8dxl-rsrc.c new file mode 100644 index 000000000..69b7aa34f --- /dev/null +++ b/drivers/clk/imx/clk-imx8dxl-rsrc.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019~2020 NXP + */ + +#include <dt-bindings/firmware/imx/rsrc.h> + +#include "clk-scu.h" + +/* Keep sorted in the ascending order */ +static u32 imx8dxl_clk_scu_rsrc_table[] = { + IMX_SC_R_SPI_0, + IMX_SC_R_SPI_1, + IMX_SC_R_SPI_2, + IMX_SC_R_SPI_3, + IMX_SC_R_UART_0, + IMX_SC_R_UART_1, + IMX_SC_R_UART_2, + IMX_SC_R_UART_3, + IMX_SC_R_I2C_0, + IMX_SC_R_I2C_1, + IMX_SC_R_I2C_2, + IMX_SC_R_I2C_3, + IMX_SC_R_ADC_0, + IMX_SC_R_FTM_0, + IMX_SC_R_FTM_1, + IMX_SC_R_CAN_0, + IMX_SC_R_LCD_0, + IMX_SC_R_LCD_0_PWM_0, + IMX_SC_R_PWM_0, + IMX_SC_R_PWM_1, + IMX_SC_R_PWM_2, + IMX_SC_R_PWM_3, + IMX_SC_R_PWM_4, + IMX_SC_R_PWM_5, + IMX_SC_R_PWM_6, + IMX_SC_R_PWM_7, + IMX_SC_R_GPT_0, + IMX_SC_R_GPT_1, + IMX_SC_R_GPT_2, + IMX_SC_R_GPT_3, + IMX_SC_R_GPT_4, + IMX_SC_R_FSPI_0, + IMX_SC_R_FSPI_1, + IMX_SC_R_SDHC_0, + IMX_SC_R_SDHC_1, + IMX_SC_R_SDHC_2, + IMX_SC_R_ENET_0, + IMX_SC_R_ENET_1, + IMX_SC_R_MLB_0, + IMX_SC_R_USB_1, + IMX_SC_R_NAND, + IMX_SC_R_M4_0_I2C, + IMX_SC_R_M4_0_UART, + IMX_SC_R_ELCDIF_PLL, + IMX_SC_R_AUDIO_PLL_0, + IMX_SC_R_AUDIO_PLL_1, + IMX_SC_R_AUDIO_CLK_0, + IMX_SC_R_AUDIO_CLK_1, + IMX_SC_R_A35 +}; + +const struct imx_clk_scu_rsrc_table imx_clk_scu_rsrc_imx8dxl = { + .rsrc = imx8dxl_clk_scu_rsrc_table, + .num = ARRAY_SIZE(imx8dxl_clk_scu_rsrc_table), +}; diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c new file mode 100644 index 000000000..b6d275855 --- /dev/null +++ b/drivers/clk/imx/clk-imx8mm.c @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP. + */ + +#include <dt-bindings/clock/imx8mm-clock.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "clk.h" + +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_sai4; +static u32 share_count_sai5; +static u32 share_count_sai6; +static u32 share_count_disp; +static u32 share_count_pdm; +static u32 share_count_nand; + +static const char *pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char *video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; +static const char *dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; +static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char *sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; + +/* CCM ROOT */ +static const char *imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", + "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; + +static const char * const imx8mm_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + +static const char *imx8mm_m4_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "sys_pll1_266m", + "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_vpu_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", + "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "vpu_pll_out", }; + +static const char *imx8mm_gpu3d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_gpu2d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", "sys_pll2_250m", + "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "sys_pll1_100m",}; + +static const char *imx8mm_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_250m", + "sys_pll2_200m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; + +static const char *imx8mm_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", "sys_pll2_200m", + "sys_pll1_133m", "sys_pll3_out", "sys_pll2_250m", "audio_pll1_out", }; + +static const char *imx8mm_vpu_bus_sels[] = {"osc_24m", "sys_pll1_800m", "vpu_pll_out", "audio_pll2_out", + "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_200m", "sys_pll1_100m", }; + +static const char *imx8mm_disp_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext4", }; + +static const char *imx8mm_disp_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll1_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", }; + +static const char *imx8mm_disp_rtrm_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll2_200m", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", "sys_pll3_out", "sys_pll2_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "sys_pll2_500m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_noc_apb_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll3_out", "sys_pll2_333m", "sys_pll2_200m", + "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", "sys_pll1_400m", + "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll2_166m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", }; + +static const char *imx8mm_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", "sys_pll2_500m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll1_out", "sys_pll1_266m", }; + +static const char *imx8mm_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_vpu_g1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_vpu_g2_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_100m", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_disp_dtrc_sels[] = {"osc_24m", "dummy", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", }; + +static const char *imx8mm_disp_dc8000_sels[] = {"osc_24m", "dummy", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll1_160m", "video_pll1_out", "sys_pll3_out", "audio_pll2_out", }; + +static const char *imx8mm_pcie1_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; + +static const char *imx8mm_pcie1_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "sys_pll1_400m", }; + +static const char *imx8mm_pcie1_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", + "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; + +static const char *imx8mm_dc_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char *imx8mm_lcdif_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char *imx8mm_sai1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", }; + +static const char *imx8mm_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_sai4_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext1", "clk_ext2", }; + +static const char *imx8mm_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext2", "clk_ext3", }; + +static const char *imx8mm_spdif2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "sys_pll1_133m", "osc_hdmi", "clk_ext3", "clk_ext4", }; + +static const char *imx8mm_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", "sys_pll2_100m", + "sys_pll1_160m", "audio_pll1_out", "video_pll1_out", "clk_ext4", }; + +static const char *imx8mm_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "video_pll1_out", }; + +static const char *imx8mm_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", "sys_pll2_200m", + "sys_pll2_500m", "video_pll1_out", "audio_pll2_out", }; + +static const char *imx8mm_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", "sys_pll1_400m", + "audio_pll2_out", "sys_pll3_out", "sys_pll2_250m", "video_pll1_out", }; + +static const char *imx8mm_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", "sys_pll2_500m", + "audio_pll2_out", "sys_pll1_266m", "sys_pll3_out", "sys_pll1_100m", }; + +static const char *imx8mm_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; + +static const char *imx8mm_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char *imx8mm_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", "sys_pll2_100m", + "sys_pll3_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll2_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll2_100m", + "sys_pll1_800m", "clk_ext2", "clk_ext4", "audio_pll2_out" }; + +static const char *imx8mm_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext1", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", "sys_pll1_40m", + "sys_pll3_out", "clk_ext2", "sys_pll1_80m", "video_pll1_out", }; + +static const char *imx8mm_gpt1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", "sys_pll1_40m", + "video_pll1_out", "sys_pll1_80m", "audio_pll1_out", "clk_ext1" }; + +static const char *imx8mm_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", "vpu_pll_out", + "sys_pll2_125m", "sys_pll3_out", "sys_pll1_80m", "sys_pll2_166m", }; + +static const char *imx8mm_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "vpu_pll_out", "sys_pll3_out", "sys_pll2_200m", + "sys_pll1_266m", "sys_pll2_500m", "sys_pll1_100m", }; + +static const char *imx8mm_dsi_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_dsi_phy_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_dsi_dbi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", "sys_pll2_500m", + "sys_pll3_out", "sys_pll1_266m", "audio_pll2_out", "sys_pll1_100m", }; + +static const char *imx8mm_csi1_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi1_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi1_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_csi2_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi2_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char *imx8mm_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_pcie2_ctrl_sels[] = {"osc_24m", "sys_pll2_250m", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll1_800m", "sys_pll2_500m", "sys_pll2_333m", "sys_pll3_out", }; + +static const char *imx8mm_pcie2_phy_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll2_500m", "clk_ext1", + "clk_ext2", "clk_ext3", "clk_ext4", "sys_pll1_400m", }; + +static const char *imx8mm_pcie2_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", "sys_pll3_out", + "sys_pll2_100m", "sys_pll1_80m", "sys_pll1_160m", "sys_pll1_200m", }; + +static const char *imx8mm_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", "sys_pll1_160m", + "sys_pll1_800m", "sys_pll3_out", "sys_pll2_250m", "audio_pll2_out", }; + +static const char *imx8mm_pdm_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char *imx8mm_vpu_h1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", "sys_pll2_1000m", + "audio_pll2_out", "sys_pll2_125m", "sys_pll3_out", "audio_pll1_out", }; + +static const char *imx8mm_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; + +static const char *imx8mm_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "dummy", "sys_pll1_200m", + "audio_pll2_out", "sys_pll2_500m", "vpu_pll", "sys_pll1_80m", }; +static const char *imx8mm_clko2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_400m", "sys_pll2_166m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "osc_32k", }; + +static const char * const clkout_sels[] = {"audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "dummy", "dummy", "gpu_pll_out", "vpu_pll_out", + "arm_pll_out", "sys_pll1", "sys_pll2", "sys_pll3", + "dummy", "dummy", "osc_24m", "dummy", "osc_32k"}; + +static struct clk_hw_onecell_data *clk_hw_data; +static struct clk_hw **hws; + +static int imx8mm_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + int ret; + + clk_hw_data = kzalloc(struct_size(clk_hw_data, hws, + IMX8MM_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return -ENOMEM; + + clk_hw_data->num = IMX8MM_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX8MM_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + hws[IMX8MM_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m"); + hws[IMX8MM_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k"); + hws[IMX8MM_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1"); + hws[IMX8MM_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2"); + hws[IMX8MM_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3"); + hws[IMX8MM_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mm-anatop"); + base = of_iomap(np, 0); + of_node_put(np); + if (WARN_ON(!base)) + return -ENOMEM; + + hws[IMX8MM_AUDIO_PLL1_REF_SEL] = imx_clk_hw_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MM_AUDIO_PLL2_REF_SEL] = imx_clk_hw_mux("audio_pll2_ref_sel", base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MM_VIDEO_PLL1_REF_SEL] = imx_clk_hw_mux("video_pll1_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MM_DRAM_PLL_REF_SEL] = imx_clk_hw_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MM_GPU_PLL_REF_SEL] = imx_clk_hw_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MM_VPU_PLL_REF_SEL] = imx_clk_hw_mux("vpu_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MM_ARM_PLL_REF_SEL] = imx_clk_hw_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MM_SYS_PLL3_REF_SEL] = imx_clk_hw_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + + hws[IMX8MM_AUDIO_PLL1] = imx_clk_hw_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll); + hws[IMX8MM_AUDIO_PLL2] = imx_clk_hw_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll); + hws[IMX8MM_VIDEO_PLL1] = imx_clk_hw_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx_1443x_pll); + hws[IMX8MM_DRAM_PLL] = imx_clk_hw_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll); + hws[IMX8MM_GPU_PLL] = imx_clk_hw_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll); + hws[IMX8MM_VPU_PLL] = imx_clk_hw_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx_1416x_pll); + hws[IMX8MM_ARM_PLL] = imx_clk_hw_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll); + hws[IMX8MM_SYS_PLL1] = imx_clk_hw_fixed("sys_pll1", 800000000); + hws[IMX8MM_SYS_PLL2] = imx_clk_hw_fixed("sys_pll2", 1000000000); + hws[IMX8MM_SYS_PLL3] = imx_clk_hw_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll); + + /* PLL bypass out */ + hws[IMX8MM_AUDIO_PLL1_BYPASS] = imx_clk_hw_mux_flags("audio_pll1_bypass", base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MM_AUDIO_PLL2_BYPASS] = imx_clk_hw_mux_flags("audio_pll2_bypass", base + 0x14, 16, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MM_VIDEO_PLL1_BYPASS] = imx_clk_hw_mux_flags("video_pll1_bypass", base + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MM_DRAM_PLL_BYPASS] = imx_clk_hw_mux_flags("dram_pll_bypass", base + 0x50, 16, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MM_GPU_PLL_BYPASS] = imx_clk_hw_mux_flags("gpu_pll_bypass", base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MM_VPU_PLL_BYPASS] = imx_clk_hw_mux_flags("vpu_pll_bypass", base + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MM_ARM_PLL_BYPASS] = imx_clk_hw_mux_flags("arm_pll_bypass", base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MM_SYS_PLL3_BYPASS] = imx_clk_hw_mux_flags("sys_pll3_bypass", base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); + + /* PLL out gate */ + hws[IMX8MM_AUDIO_PLL1_OUT] = imx_clk_hw_gate("audio_pll1_out", "audio_pll1_bypass", base, 13); + hws[IMX8MM_AUDIO_PLL2_OUT] = imx_clk_hw_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x14, 13); + hws[IMX8MM_VIDEO_PLL1_OUT] = imx_clk_hw_gate("video_pll1_out", "video_pll1_bypass", base + 0x28, 13); + hws[IMX8MM_DRAM_PLL_OUT] = imx_clk_hw_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13); + hws[IMX8MM_GPU_PLL_OUT] = imx_clk_hw_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 11); + hws[IMX8MM_VPU_PLL_OUT] = imx_clk_hw_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 11); + hws[IMX8MM_ARM_PLL_OUT] = imx_clk_hw_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11); + hws[IMX8MM_SYS_PLL3_OUT] = imx_clk_hw_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11); + + /* SYS PLL1 fixed output */ + hws[IMX8MM_SYS_PLL1_OUT] = imx_clk_hw_gate("sys_pll1_out", "sys_pll1", base + 0x94, 11); + + hws[IMX8MM_SYS_PLL1_40M] = imx_clk_hw_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20); + hws[IMX8MM_SYS_PLL1_80M] = imx_clk_hw_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10); + hws[IMX8MM_SYS_PLL1_100M] = imx_clk_hw_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8); + hws[IMX8MM_SYS_PLL1_133M] = imx_clk_hw_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6); + hws[IMX8MM_SYS_PLL1_160M] = imx_clk_hw_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5); + hws[IMX8MM_SYS_PLL1_200M] = imx_clk_hw_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4); + hws[IMX8MM_SYS_PLL1_266M] = imx_clk_hw_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3); + hws[IMX8MM_SYS_PLL1_400M] = imx_clk_hw_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2); + hws[IMX8MM_SYS_PLL1_800M] = imx_clk_hw_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); + + /* SYS PLL2 fixed output */ + hws[IMX8MM_SYS_PLL2_OUT] = imx_clk_hw_gate("sys_pll2_out", "sys_pll2", base + 0x104, 11); + hws[IMX8MM_SYS_PLL2_50M] = imx_clk_hw_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20); + hws[IMX8MM_SYS_PLL2_100M] = imx_clk_hw_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10); + hws[IMX8MM_SYS_PLL2_125M] = imx_clk_hw_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8); + hws[IMX8MM_SYS_PLL2_166M] = imx_clk_hw_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6); + hws[IMX8MM_SYS_PLL2_200M] = imx_clk_hw_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5); + hws[IMX8MM_SYS_PLL2_250M] = imx_clk_hw_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4); + hws[IMX8MM_SYS_PLL2_333M] = imx_clk_hw_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3); + hws[IMX8MM_SYS_PLL2_500M] = imx_clk_hw_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2); + hws[IMX8MM_SYS_PLL2_1000M] = imx_clk_hw_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); + + hws[IMX8MM_CLK_CLKOUT1_SEL] = imx_clk_hw_mux2("clkout1_sel", base + 0x128, 4, 4, clkout_sels, ARRAY_SIZE(clkout_sels)); + hws[IMX8MM_CLK_CLKOUT1_DIV] = imx_clk_hw_divider("clkout1_div", "clkout1_sel", base + 0x128, 0, 4); + hws[IMX8MM_CLK_CLKOUT1] = imx_clk_hw_gate("clkout1", "clkout1_div", base + 0x128, 8); + hws[IMX8MM_CLK_CLKOUT2_SEL] = imx_clk_hw_mux2("clkout2_sel", base + 0x128, 20, 4, clkout_sels, ARRAY_SIZE(clkout_sels)); + hws[IMX8MM_CLK_CLKOUT2_DIV] = imx_clk_hw_divider("clkout2_div", "clkout2_sel", base + 0x128, 16, 4); + hws[IMX8MM_CLK_CLKOUT2] = imx_clk_hw_gate("clkout2", "clkout2_div", base + 0x128, 24); + + np = dev->of_node; + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + /* Core Slice */ + hws[IMX8MM_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mm_a53_sels, base + 0x8000); + hws[IMX8MM_CLK_A53_CG] = hws[IMX8MM_CLK_A53_DIV]; + hws[IMX8MM_CLK_A53_SRC] = hws[IMX8MM_CLK_A53_DIV]; + + hws[IMX8MM_CLK_M4_CORE] = imx8m_clk_hw_composite_core("arm_m4_core", imx8mm_m4_sels, base + 0x8080); + hws[IMX8MM_CLK_VPU_CORE] = imx8m_clk_hw_composite_core("vpu_core", imx8mm_vpu_sels, base + 0x8100); + hws[IMX8MM_CLK_GPU3D_CORE] = imx8m_clk_hw_composite_core("gpu3d_core", imx8mm_gpu3d_sels, base + 0x8180); + hws[IMX8MM_CLK_GPU2D_CORE] = imx8m_clk_hw_composite_core("gpu2d_core", imx8mm_gpu2d_sels, base + 0x8200); + + /* For backwards compatibility */ + hws[IMX8MM_CLK_M4_SRC] = hws[IMX8MM_CLK_M4_CORE]; + hws[IMX8MM_CLK_M4_CG] = hws[IMX8MM_CLK_M4_CORE]; + hws[IMX8MM_CLK_M4_DIV] = hws[IMX8MM_CLK_M4_CORE]; + hws[IMX8MM_CLK_VPU_SRC] = hws[IMX8MM_CLK_VPU_CORE]; + hws[IMX8MM_CLK_VPU_CG] = hws[IMX8MM_CLK_VPU_CORE]; + hws[IMX8MM_CLK_VPU_DIV] = hws[IMX8MM_CLK_VPU_CORE]; + hws[IMX8MM_CLK_GPU3D_SRC] = hws[IMX8MM_CLK_GPU3D_CORE]; + hws[IMX8MM_CLK_GPU3D_CG] = hws[IMX8MM_CLK_GPU3D_CORE]; + hws[IMX8MM_CLK_GPU3D_DIV] = hws[IMX8MM_CLK_GPU3D_CORE]; + hws[IMX8MM_CLK_GPU2D_SRC] = hws[IMX8MM_CLK_GPU2D_CORE]; + hws[IMX8MM_CLK_GPU2D_CG] = hws[IMX8MM_CLK_GPU2D_CORE]; + hws[IMX8MM_CLK_GPU2D_DIV] = hws[IMX8MM_CLK_GPU2D_CORE]; + + /* CORE SEL */ + hws[IMX8MM_CLK_A53_CORE] = imx_clk_hw_mux2("arm_a53_core", base + 0x9880, 24, 1, imx8mm_a53_core_sels, ARRAY_SIZE(imx8mm_a53_core_sels)); + + /* BUS */ + hws[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mm_main_axi_sels, base + 0x8800); + hws[IMX8MM_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mm_enet_axi_sels, base + 0x8880); + hws[IMX8MM_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus_critical("nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900); + hws[IMX8MM_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mm_vpu_bus_sels, base + 0x8980); + hws[IMX8MM_CLK_DISP_AXI] = imx8m_clk_hw_composite_bus("disp_axi", imx8mm_disp_axi_sels, base + 0x8a00); + hws[IMX8MM_CLK_DISP_APB] = imx8m_clk_hw_composite_bus("disp_apb", imx8mm_disp_apb_sels, base + 0x8a80); + hws[IMX8MM_CLK_DISP_RTRM] = imx8m_clk_hw_composite_bus("disp_rtrm", imx8mm_disp_rtrm_sels, base + 0x8b00); + hws[IMX8MM_CLK_USB_BUS] = imx8m_clk_hw_composite_bus("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80); + hws[IMX8MM_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mm_gpu_axi_sels, base + 0x8c00); + hws[IMX8MM_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mm_gpu_ahb_sels, base + 0x8c80); + hws[IMX8MM_CLK_NOC] = imx8m_clk_hw_composite_bus_critical("noc", imx8mm_noc_sels, base + 0x8d00); + hws[IMX8MM_CLK_NOC_APB] = imx8m_clk_hw_composite_bus_critical("noc_apb", imx8mm_noc_apb_sels, base + 0x8d80); + + /* AHB */ + hws[IMX8MM_CLK_AHB] = imx8m_clk_hw_composite_bus_critical("ahb", imx8mm_ahb_sels, base + 0x9000); + hws[IMX8MM_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mm_audio_ahb_sels, base + 0x9100); + + /* IPG */ + hws[IMX8MM_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb", base + 0x9080, 0, 1); + hws[IMX8MM_CLK_IPG_AUDIO_ROOT] = imx_clk_hw_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1); + + /* + * DRAM clocks are manipulated from TF-A outside clock framework. + * The fw_managed helper sets GET_RATE_NOCACHE and clears SET_PARENT_GATE + * as div value should always be read from hardware + */ + hws[IMX8MM_CLK_DRAM_ALT] = imx8m_clk_hw_fw_managed_composite("dram_alt", imx8mm_dram_alt_sels, base + 0xa000); + hws[IMX8MM_CLK_DRAM_APB] = imx8m_clk_hw_fw_managed_composite_critical("dram_apb", imx8mm_dram_apb_sels, base + 0xa080); + + /* IP */ + hws[IMX8MM_CLK_VPU_G1] = imx8m_clk_hw_composite("vpu_g1", imx8mm_vpu_g1_sels, base + 0xa100); + hws[IMX8MM_CLK_VPU_G2] = imx8m_clk_hw_composite("vpu_g2", imx8mm_vpu_g2_sels, base + 0xa180); + hws[IMX8MM_CLK_DISP_DTRC] = imx8m_clk_hw_composite("disp_dtrc", imx8mm_disp_dtrc_sels, base + 0xa200); + hws[IMX8MM_CLK_DISP_DC8000] = imx8m_clk_hw_composite("disp_dc8000", imx8mm_disp_dc8000_sels, base + 0xa280); + hws[IMX8MM_CLK_PCIE1_CTRL] = imx8m_clk_hw_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, base + 0xa300); + hws[IMX8MM_CLK_PCIE1_PHY] = imx8m_clk_hw_composite("pcie1_phy", imx8mm_pcie1_phy_sels, base + 0xa380); + hws[IMX8MM_CLK_PCIE1_AUX] = imx8m_clk_hw_composite("pcie1_aux", imx8mm_pcie1_aux_sels, base + 0xa400); + hws[IMX8MM_CLK_DC_PIXEL] = imx8m_clk_hw_composite("dc_pixel", imx8mm_dc_pixel_sels, base + 0xa480); + hws[IMX8MM_CLK_LCDIF_PIXEL] = imx8m_clk_hw_composite("lcdif_pixel", imx8mm_lcdif_pixel_sels, base + 0xa500); + hws[IMX8MM_CLK_SAI1] = imx8m_clk_hw_composite("sai1", imx8mm_sai1_sels, base + 0xa580); + hws[IMX8MM_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mm_sai2_sels, base + 0xa600); + hws[IMX8MM_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mm_sai3_sels, base + 0xa680); + hws[IMX8MM_CLK_SAI4] = imx8m_clk_hw_composite("sai4", imx8mm_sai4_sels, base + 0xa700); + hws[IMX8MM_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mm_sai5_sels, base + 0xa780); + hws[IMX8MM_CLK_SAI6] = imx8m_clk_hw_composite("sai6", imx8mm_sai6_sels, base + 0xa800); + hws[IMX8MM_CLK_SPDIF1] = imx8m_clk_hw_composite("spdif1", imx8mm_spdif1_sels, base + 0xa880); + hws[IMX8MM_CLK_SPDIF2] = imx8m_clk_hw_composite("spdif2", imx8mm_spdif2_sels, base + 0xa900); + hws[IMX8MM_CLK_ENET_REF] = imx8m_clk_hw_composite("enet_ref", imx8mm_enet_ref_sels, base + 0xa980); + hws[IMX8MM_CLK_ENET_TIMER] = imx8m_clk_hw_composite("enet_timer", imx8mm_enet_timer_sels, base + 0xaa00); + hws[IMX8MM_CLK_ENET_PHY_REF] = imx8m_clk_hw_composite("enet_phy", imx8mm_enet_phy_sels, base + 0xaa80); + hws[IMX8MM_CLK_NAND] = imx8m_clk_hw_composite("nand", imx8mm_nand_sels, base + 0xab00); + hws[IMX8MM_CLK_QSPI] = imx8m_clk_hw_composite("qspi", imx8mm_qspi_sels, base + 0xab80); + hws[IMX8MM_CLK_USDHC1] = imx8m_clk_hw_composite("usdhc1", imx8mm_usdhc1_sels, base + 0xac00); + hws[IMX8MM_CLK_USDHC2] = imx8m_clk_hw_composite("usdhc2", imx8mm_usdhc2_sels, base + 0xac80); + hws[IMX8MM_CLK_I2C1] = imx8m_clk_hw_composite("i2c1", imx8mm_i2c1_sels, base + 0xad00); + hws[IMX8MM_CLK_I2C2] = imx8m_clk_hw_composite("i2c2", imx8mm_i2c2_sels, base + 0xad80); + hws[IMX8MM_CLK_I2C3] = imx8m_clk_hw_composite("i2c3", imx8mm_i2c3_sels, base + 0xae00); + hws[IMX8MM_CLK_I2C4] = imx8m_clk_hw_composite("i2c4", imx8mm_i2c4_sels, base + 0xae80); + hws[IMX8MM_CLK_UART1] = imx8m_clk_hw_composite("uart1", imx8mm_uart1_sels, base + 0xaf00); + hws[IMX8MM_CLK_UART2] = imx8m_clk_hw_composite("uart2", imx8mm_uart2_sels, base + 0xaf80); + hws[IMX8MM_CLK_UART3] = imx8m_clk_hw_composite("uart3", imx8mm_uart3_sels, base + 0xb000); + hws[IMX8MM_CLK_UART4] = imx8m_clk_hw_composite("uart4", imx8mm_uart4_sels, base + 0xb080); + hws[IMX8MM_CLK_USB_CORE_REF] = imx8m_clk_hw_composite("usb_core_ref", imx8mm_usb_core_sels, base + 0xb100); + hws[IMX8MM_CLK_USB_PHY_REF] = imx8m_clk_hw_composite("usb_phy_ref", imx8mm_usb_phy_sels, base + 0xb180); + hws[IMX8MM_CLK_GIC] = imx8m_clk_hw_composite_critical("gic", imx8mm_gic_sels, base + 0xb200); + hws[IMX8MM_CLK_ECSPI1] = imx8m_clk_hw_composite("ecspi1", imx8mm_ecspi1_sels, base + 0xb280); + hws[IMX8MM_CLK_ECSPI2] = imx8m_clk_hw_composite("ecspi2", imx8mm_ecspi2_sels, base + 0xb300); + hws[IMX8MM_CLK_PWM1] = imx8m_clk_hw_composite("pwm1", imx8mm_pwm1_sels, base + 0xb380); + hws[IMX8MM_CLK_PWM2] = imx8m_clk_hw_composite("pwm2", imx8mm_pwm2_sels, base + 0xb400); + hws[IMX8MM_CLK_PWM3] = imx8m_clk_hw_composite("pwm3", imx8mm_pwm3_sels, base + 0xb480); + hws[IMX8MM_CLK_PWM4] = imx8m_clk_hw_composite("pwm4", imx8mm_pwm4_sels, base + 0xb500); + hws[IMX8MM_CLK_GPT1] = imx8m_clk_hw_composite("gpt1", imx8mm_gpt1_sels, base + 0xb580); + hws[IMX8MM_CLK_WDOG] = imx8m_clk_hw_composite("wdog", imx8mm_wdog_sels, base + 0xb900); + hws[IMX8MM_CLK_WRCLK] = imx8m_clk_hw_composite("wrclk", imx8mm_wrclk_sels, base + 0xb980); + hws[IMX8MM_CLK_CLKO1] = imx8m_clk_hw_composite("clko1", imx8mm_clko1_sels, base + 0xba00); + hws[IMX8MM_CLK_CLKO2] = imx8m_clk_hw_composite("clko2", imx8mm_clko2_sels, base + 0xba80); + hws[IMX8MM_CLK_DSI_CORE] = imx8m_clk_hw_composite("dsi_core", imx8mm_dsi_core_sels, base + 0xbb00); + hws[IMX8MM_CLK_DSI_PHY_REF] = imx8m_clk_hw_composite("dsi_phy_ref", imx8mm_dsi_phy_sels, base + 0xbb80); + hws[IMX8MM_CLK_DSI_DBI] = imx8m_clk_hw_composite("dsi_dbi", imx8mm_dsi_dbi_sels, base + 0xbc00); + hws[IMX8MM_CLK_USDHC3] = imx8m_clk_hw_composite("usdhc3", imx8mm_usdhc3_sels, base + 0xbc80); + hws[IMX8MM_CLK_CSI1_CORE] = imx8m_clk_hw_composite("csi1_core", imx8mm_csi1_core_sels, base + 0xbd00); + hws[IMX8MM_CLK_CSI1_PHY_REF] = imx8m_clk_hw_composite("csi1_phy_ref", imx8mm_csi1_phy_sels, base + 0xbd80); + hws[IMX8MM_CLK_CSI1_ESC] = imx8m_clk_hw_composite("csi1_esc", imx8mm_csi1_esc_sels, base + 0xbe00); + hws[IMX8MM_CLK_CSI2_CORE] = imx8m_clk_hw_composite("csi2_core", imx8mm_csi2_core_sels, base + 0xbe80); + hws[IMX8MM_CLK_CSI2_PHY_REF] = imx8m_clk_hw_composite("csi2_phy_ref", imx8mm_csi2_phy_sels, base + 0xbf00); + hws[IMX8MM_CLK_CSI2_ESC] = imx8m_clk_hw_composite("csi2_esc", imx8mm_csi2_esc_sels, base + 0xbf80); + hws[IMX8MM_CLK_PCIE2_CTRL] = imx8m_clk_hw_composite("pcie2_ctrl", imx8mm_pcie2_ctrl_sels, base + 0xc000); + hws[IMX8MM_CLK_PCIE2_PHY] = imx8m_clk_hw_composite("pcie2_phy", imx8mm_pcie2_phy_sels, base + 0xc080); + hws[IMX8MM_CLK_PCIE2_AUX] = imx8m_clk_hw_composite("pcie2_aux", imx8mm_pcie2_aux_sels, base + 0xc100); + hws[IMX8MM_CLK_ECSPI3] = imx8m_clk_hw_composite("ecspi3", imx8mm_ecspi3_sels, base + 0xc180); + hws[IMX8MM_CLK_PDM] = imx8m_clk_hw_composite("pdm", imx8mm_pdm_sels, base + 0xc200); + hws[IMX8MM_CLK_VPU_H1] = imx8m_clk_hw_composite("vpu_h1", imx8mm_vpu_h1_sels, base + 0xc280); + + /* CCGR */ + hws[IMX8MM_CLK_ECSPI1_ROOT] = imx_clk_hw_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0); + hws[IMX8MM_CLK_ECSPI2_ROOT] = imx_clk_hw_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0); + hws[IMX8MM_CLK_ECSPI3_ROOT] = imx_clk_hw_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0); + hws[IMX8MM_CLK_ENET1_ROOT] = imx_clk_hw_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0); + hws[IMX8MM_CLK_GPIO1_ROOT] = imx_clk_hw_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0); + hws[IMX8MM_CLK_GPIO2_ROOT] = imx_clk_hw_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0); + hws[IMX8MM_CLK_GPIO3_ROOT] = imx_clk_hw_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0); + hws[IMX8MM_CLK_GPIO4_ROOT] = imx_clk_hw_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0); + hws[IMX8MM_CLK_GPIO5_ROOT] = imx_clk_hw_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0); + hws[IMX8MM_CLK_GPT1_ROOT] = imx_clk_hw_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0); + hws[IMX8MM_CLK_I2C1_ROOT] = imx_clk_hw_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0); + hws[IMX8MM_CLK_I2C2_ROOT] = imx_clk_hw_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0); + hws[IMX8MM_CLK_I2C3_ROOT] = imx_clk_hw_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0); + hws[IMX8MM_CLK_I2C4_ROOT] = imx_clk_hw_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0); + hws[IMX8MM_CLK_MU_ROOT] = imx_clk_hw_gate4("mu_root_clk", "ipg_root", base + 0x4210, 0); + hws[IMX8MM_CLK_OCOTP_ROOT] = imx_clk_hw_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0); + hws[IMX8MM_CLK_PCIE1_ROOT] = imx_clk_hw_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0); + hws[IMX8MM_CLK_PWM1_ROOT] = imx_clk_hw_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0); + hws[IMX8MM_CLK_PWM2_ROOT] = imx_clk_hw_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0); + hws[IMX8MM_CLK_PWM3_ROOT] = imx_clk_hw_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0); + hws[IMX8MM_CLK_PWM4_ROOT] = imx_clk_hw_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0); + hws[IMX8MM_CLK_QSPI_ROOT] = imx_clk_hw_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0); + hws[IMX8MM_CLK_NAND_ROOT] = imx_clk_hw_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand); + hws[IMX8MM_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand); + hws[IMX8MM_CLK_SAI1_ROOT] = imx_clk_hw_gate2_shared2("sai1_root_clk", "sai1", base + 0x4330, 0, &share_count_sai1); + hws[IMX8MM_CLK_SAI1_IPG] = imx_clk_hw_gate2_shared2("sai1_ipg_clk", "ipg_audio_root", base + 0x4330, 0, &share_count_sai1); + hws[IMX8MM_CLK_SAI2_ROOT] = imx_clk_hw_gate2_shared2("sai2_root_clk", "sai2", base + 0x4340, 0, &share_count_sai2); + hws[IMX8MM_CLK_SAI2_IPG] = imx_clk_hw_gate2_shared2("sai2_ipg_clk", "ipg_audio_root", base + 0x4340, 0, &share_count_sai2); + hws[IMX8MM_CLK_SAI3_ROOT] = imx_clk_hw_gate2_shared2("sai3_root_clk", "sai3", base + 0x4350, 0, &share_count_sai3); + hws[IMX8MM_CLK_SAI3_IPG] = imx_clk_hw_gate2_shared2("sai3_ipg_clk", "ipg_audio_root", base + 0x4350, 0, &share_count_sai3); + hws[IMX8MM_CLK_SAI4_ROOT] = imx_clk_hw_gate2_shared2("sai4_root_clk", "sai4", base + 0x4360, 0, &share_count_sai4); + hws[IMX8MM_CLK_SAI4_IPG] = imx_clk_hw_gate2_shared2("sai4_ipg_clk", "ipg_audio_root", base + 0x4360, 0, &share_count_sai4); + hws[IMX8MM_CLK_SAI5_ROOT] = imx_clk_hw_gate2_shared2("sai5_root_clk", "sai5", base + 0x4370, 0, &share_count_sai5); + hws[IMX8MM_CLK_SAI5_IPG] = imx_clk_hw_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", base + 0x4370, 0, &share_count_sai5); + hws[IMX8MM_CLK_SAI6_ROOT] = imx_clk_hw_gate2_shared2("sai6_root_clk", "sai6", base + 0x4380, 0, &share_count_sai6); + hws[IMX8MM_CLK_SAI6_IPG] = imx_clk_hw_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", base + 0x4380, 0, &share_count_sai6); + hws[IMX8MM_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", base + 0x4490, 0); + hws[IMX8MM_CLK_UART2_ROOT] = imx_clk_hw_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0); + hws[IMX8MM_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); + hws[IMX8MM_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); + hws[IMX8MM_CLK_USB1_CTRL_ROOT] = imx_clk_hw_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0); + hws[IMX8MM_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core", base + 0x44f0, 0); + hws[IMX8MM_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); + hws[IMX8MM_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); + hws[IMX8MM_CLK_WDOG1_ROOT] = imx_clk_hw_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0); + hws[IMX8MM_CLK_WDOG2_ROOT] = imx_clk_hw_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0); + hws[IMX8MM_CLK_WDOG3_ROOT] = imx_clk_hw_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0); + hws[IMX8MM_CLK_VPU_G1_ROOT] = imx_clk_hw_gate4("vpu_g1_root_clk", "vpu_g1", base + 0x4560, 0); + hws[IMX8MM_CLK_GPU_BUS_ROOT] = imx_clk_hw_gate4("gpu_root_clk", "gpu_axi", base + 0x4570, 0); + hws[IMX8MM_CLK_VPU_H1_ROOT] = imx_clk_hw_gate4("vpu_h1_root_clk", "vpu_h1", base + 0x4590, 0); + hws[IMX8MM_CLK_VPU_G2_ROOT] = imx_clk_hw_gate4("vpu_g2_root_clk", "vpu_g2", base + 0x45a0, 0); + hws[IMX8MM_CLK_PDM_ROOT] = imx_clk_hw_gate2_shared2("pdm_root_clk", "pdm", base + 0x45b0, 0, &share_count_pdm); + hws[IMX8MM_CLK_PDM_IPG] = imx_clk_hw_gate2_shared2("pdm_ipg_clk", "ipg_audio_root", base + 0x45b0, 0, &share_count_pdm); + hws[IMX8MM_CLK_DISP_ROOT] = imx_clk_hw_gate2_shared2("disp_root_clk", "disp_dc8000", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MM_CLK_DISP_AXI_ROOT] = imx_clk_hw_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MM_CLK_DISP_APB_ROOT] = imx_clk_hw_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MM_CLK_DISP_RTRM_ROOT] = imx_clk_hw_gate2_shared2("disp_rtrm_root_clk", "disp_rtrm", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MM_CLK_USDHC3_ROOT] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0); + hws[IMX8MM_CLK_TMU_ROOT] = imx_clk_hw_gate4("tmu_root_clk", "ipg_root", base + 0x4620, 0); + hws[IMX8MM_CLK_VPU_DEC_ROOT] = imx_clk_hw_gate4("vpu_dec_root_clk", "vpu_bus", base + 0x4630, 0); + hws[IMX8MM_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0); + hws[IMX8MM_CLK_SDMA2_ROOT] = imx_clk_hw_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0); + hws[IMX8MM_CLK_SDMA3_ROOT] = imx_clk_hw_gate4("sdma3_clk", "ipg_audio_root", base + 0x45f0, 0); + hws[IMX8MM_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_core", base + 0x4660, 0); + hws[IMX8MM_CLK_CSI1_ROOT] = imx_clk_hw_gate4("csi1_root_clk", "csi1_core", base + 0x4650, 0); + + hws[IMX8MM_CLK_GPT_3M] = imx_clk_hw_fixed_factor("gpt_3m", "osc_24m", 1, 8); + + hws[IMX8MM_CLK_DRAM_ALT_ROOT] = imx_clk_hw_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + hws[IMX8MM_CLK_DRAM_CORE] = imx_clk_hw_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mm_dram_core_sels, ARRAY_SIZE(imx8mm_dram_core_sels), CLK_IS_CRITICAL); + + hws[IMX8MM_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MM_CLK_A53_CORE]->clk, + hws[IMX8MM_CLK_A53_CORE]->clk, + hws[IMX8MM_ARM_PLL_OUT]->clk, + hws[IMX8MM_CLK_A53_DIV]->clk); + + imx_check_clk_hws(hws, IMX8MM_CLK_END); + + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + if (ret < 0) { + dev_err(dev, "failed to register clks for i.MX8MM\n"); + goto unregister_hws; + } + + imx_register_uart_clocks(4); + + return 0; + +unregister_hws: + imx_unregister_hw_clocks(hws, IMX8MM_CLK_END); + + return ret; +} + +static const struct of_device_id imx8mm_clk_of_match[] = { + { .compatible = "fsl,imx8mm-ccm" }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx8mm_clk_of_match); + +static struct platform_driver imx8mm_clk_driver = { + .probe = imx8mm_clocks_probe, + .driver = { + .name = "imx8mm-ccm", + /* + * Disable bind attributes: clocks are not removed and + * reloading the driver will crash or break devices. + */ + .suppress_bind_attrs = true, + .of_match_table = imx8mm_clk_of_match, + }, +}; +module_platform_driver(imx8mm_clk_driver); +module_param(mcore_booted, bool, S_IRUGO); +MODULE_PARM_DESC(mcore_booted, "See Cortex-M core is booted or not"); + +MODULE_AUTHOR("Bai Ping <ping.bai@nxp.com>"); +MODULE_DESCRIPTION("NXP i.MX8MM clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c new file mode 100644 index 000000000..b2b8b8b3f --- /dev/null +++ b/drivers/clk/imx/clk-imx8mn.c @@ -0,0 +1,639 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018-2019 NXP. + */ + +#include <dt-bindings/clock/imx8mn-clock.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "clk.h" + +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_sai5; +static u32 share_count_sai6; +static u32 share_count_sai7; +static u32 share_count_disp; +static u32 share_count_pdm; +static u32 share_count_nand; + +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char * const video_pll_bypass_sels[] = {"video_pll", "video_pll_ref_sel", }; +static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; +static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char * const m7_alt_pll_bypass_sels[] = {"m7_alt_pll", "m7_alt_pll_ref_sel", }; +static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; + +static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", + "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", + "audio_pll1_out", "sys_pll3_out", }; + +static const char * const imx8mn_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + +static const char * const imx8mn_m7_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "m7_alt_pll_out", + "sys_pll1_800m", "audio_pll1_out", "video_pll_out", "sys_pll3_out", }; + +static const char * const imx8mn_gpu_core_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll_out", "audio_pll2_out", }; + +static const char * const imx8mn_gpu_shader_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll_out", "audio_pll2_out", }; + +static const char * const imx8mn_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", + "sys_pll2_250m", "sys_pll2_1000m", "audio_pll1_out", + "video_pll_out", "sys_pll1_100m",}; + +static const char * const imx8mn_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", + "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", + "video_pll_out", "sys_pll3_out", }; + +static const char * const imx8mn_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", + "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll1_out", }; + +static const char * const imx8mn_disp_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "clk_ext4", }; + +static const char * const imx8mn_disp_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "clk_ext3", }; + +static const char * const imx8mn_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll_out", "audio_pll2_out", }; + +static const char * const imx8mn_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll_out", "audio_pll2_out", }; + +static const char * const imx8mn_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", + "video_pll_out", "audio_pll2_out", }; + +static const char * const imx8mn_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", + "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", + "audio_pll1_out", "video_pll_out", }; + +static const char * const imx8mn_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll2_166m", "sys_pll3_out", + "audio_pll1_out", "video_pll_out", }; + +static const char * const imx8mn_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", + "sys_pll2_500m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll1_out", "sys_pll1_266m", }; + +static const char * const imx8mn_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_disp_pixel_sels[] = {"osc_24m", "video_pll_out", "audio_pll2_out", + "audio_pll1_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll3_out", "clk_ext4", }; + +static const char * const imx8mn_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll_out", "sys_pll1_133m", "dummy", + "clk_ext2", "clk_ext3", }; + +static const char * const imx8mn_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll_out", "sys_pll1_133m", "dummy", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mn_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll_out", "sys_pll1_133m", "dummy", + "clk_ext2", "clk_ext3", }; + +static const char * const imx8mn_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll_out", "sys_pll1_133m", "dummy", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mn_sai7_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll_out", "sys_pll1_133m", "dummy", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mn_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll_out", "sys_pll1_133m", "dummy", + "clk_ext2", "clk_ext3", }; + +static const char * const imx8mn_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", + "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", + "video_pll_out", "clk_ext4", }; + +static const char * const imx8mn_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", + "clk_ext1", "clk_ext2", "clk_ext3", + "clk_ext4", "video_pll_out", }; + +static const char * const imx8mn_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", + "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out", + "video_pll_out", "audio_pll2_out", }; + +static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", + "sys_pll1_400m", "audio_pll2_out", "sys_pll3_out", + "sys_pll2_250m", "video_pll_out", }; + +static const char * const imx8mn_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", + "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", + "sys_pll3_out", "sys_pll1_100m", }; + +static const char * const imx8mn_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mn_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mn_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll1_800m", "clk_ext2", + "clk_ext4", "audio_pll2_out" }; + +static const char * const imx8mn_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext1", + "sys_pll1_80m", "video_pll_out", }; + +static const char * const imx8mn_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext1", + "sys_pll1_80m", "video_pll_out", }; + +static const char * const imx8mn_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext2", + "sys_pll1_80m", "video_pll_out", }; + +static const char * const imx8mn_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext2", + "sys_pll1_80m", "video_pll_out", }; + +static const char * const imx8mn_gpt1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1", }; + +static const char * const imx8mn_gpt2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1", }; + +static const char * const imx8mn_gpt3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1", }; + +static const char * const imx8mn_gpt4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1", }; + +static const char * const imx8mn_gpt5_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1", }; + +static const char * const imx8mn_gpt6_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1", }; + +static const char * const imx8mn_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", + "m7_alt_pll_out", "sys_pll2_125m", "sys_pll3_out", + "sys_pll1_80m", "sys_pll2_166m", }; + +static const char * const imx8mn_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "m7_alt_pll_out", + "sys_pll3_out", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll2_500m", "sys_pll1_100m", }; + +static const char * const imx8mn_dsi_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_dsi_phy_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "clk_ext2", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_dsi_dbi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mn_camera_pixel_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_csi1_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "clk_ext2", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_csi2_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "clk_ext2", + "audio_pll2_out", "video_pll_out", }; + +static const char * const imx8mn_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_pdm_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; + +static const char * const imx8mn_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "dummy", + "sys_pll1_200m", "audio_pll2_out", "sys_pll2_500m", + "dummy", "sys_pll1_80m", }; +static const char * const imx8mn_clko2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_400m", + "sys_pll2_166m", "sys_pll3_out", "audio_pll1_out", + "video_pll_out", "osc_32k", }; + +static const char * const clkout_sels[] = {"audio_pll1_out", "audio_pll2_out", "video_pll_out", + "dummy", "dummy", "gpu_pll_out", "dummy", + "arm_pll_out", "sys_pll1", "sys_pll2", "sys_pll3", + "dummy", "dummy", "osc_24m", "dummy", "osc_32k"}; + +static struct clk_hw_onecell_data *clk_hw_data; +static struct clk_hw **hws; + +static int imx8mn_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + int ret; + + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, + IMX8MN_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return -ENOMEM; + + clk_hw_data->num = IMX8MN_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX8MN_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + hws[IMX8MN_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m"); + hws[IMX8MN_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k"); + hws[IMX8MN_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1"); + hws[IMX8MN_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2"); + hws[IMX8MN_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3"); + hws[IMX8MN_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mn-anatop"); + base = devm_of_iomap(dev, np, 0, NULL); + of_node_put(np); + if (WARN_ON(IS_ERR(base))) { + ret = PTR_ERR(base); + goto unregister_hws; + } + + hws[IMX8MN_AUDIO_PLL1_REF_SEL] = imx_clk_hw_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MN_AUDIO_PLL2_REF_SEL] = imx_clk_hw_mux("audio_pll2_ref_sel", base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MN_VIDEO_PLL_REF_SEL] = imx_clk_hw_mux("video_pll_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MN_DRAM_PLL_REF_SEL] = imx_clk_hw_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MN_GPU_PLL_REF_SEL] = imx_clk_hw_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MN_M7_ALT_PLL_REF_SEL] = imx_clk_hw_mux("m7_alt_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MN_ARM_PLL_REF_SEL] = imx_clk_hw_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MN_SYS_PLL3_REF_SEL] = imx_clk_hw_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + + hws[IMX8MN_AUDIO_PLL1] = imx_clk_hw_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll); + hws[IMX8MN_AUDIO_PLL2] = imx_clk_hw_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll); + hws[IMX8MN_VIDEO_PLL] = imx_clk_hw_pll14xx("video_pll", "video_pll_ref_sel", base + 0x28, &imx_1443x_pll); + hws[IMX8MN_DRAM_PLL] = imx_clk_hw_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll); + hws[IMX8MN_GPU_PLL] = imx_clk_hw_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll); + hws[IMX8MN_M7_ALT_PLL] = imx_clk_hw_pll14xx("m7_alt_pll", "m7_alt_pll_ref_sel", base + 0x74, &imx_1416x_pll); + hws[IMX8MN_ARM_PLL] = imx_clk_hw_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll); + hws[IMX8MN_SYS_PLL1] = imx_clk_hw_fixed("sys_pll1", 800000000); + hws[IMX8MN_SYS_PLL2] = imx_clk_hw_fixed("sys_pll2", 1000000000); + hws[IMX8MN_SYS_PLL3] = imx_clk_hw_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll); + + /* PLL bypass out */ + hws[IMX8MN_AUDIO_PLL1_BYPASS] = imx_clk_hw_mux_flags("audio_pll1_bypass", base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MN_AUDIO_PLL2_BYPASS] = imx_clk_hw_mux_flags("audio_pll2_bypass", base + 0x14, 16, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MN_VIDEO_PLL_BYPASS] = imx_clk_hw_mux_flags("video_pll_bypass", base + 0x28, 16, 1, video_pll_bypass_sels, ARRAY_SIZE(video_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MN_DRAM_PLL_BYPASS] = imx_clk_hw_mux_flags("dram_pll_bypass", base + 0x50, 16, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MN_GPU_PLL_BYPASS] = imx_clk_hw_mux_flags("gpu_pll_bypass", base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MN_M7_ALT_PLL_BYPASS] = imx_clk_hw_mux_flags("m7_alt_pll_bypass", base + 0x74, 28, 1, m7_alt_pll_bypass_sels, ARRAY_SIZE(m7_alt_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MN_ARM_PLL_BYPASS] = imx_clk_hw_mux_flags("arm_pll_bypass", base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MN_SYS_PLL3_BYPASS] = imx_clk_hw_mux_flags("sys_pll3_bypass", base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); + + /* PLL out gate */ + hws[IMX8MN_AUDIO_PLL1_OUT] = imx_clk_hw_gate("audio_pll1_out", "audio_pll1_bypass", base, 13); + hws[IMX8MN_AUDIO_PLL2_OUT] = imx_clk_hw_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x14, 13); + hws[IMX8MN_VIDEO_PLL_OUT] = imx_clk_hw_gate("video_pll_out", "video_pll_bypass", base + 0x28, 13); + hws[IMX8MN_DRAM_PLL_OUT] = imx_clk_hw_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13); + hws[IMX8MN_GPU_PLL_OUT] = imx_clk_hw_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 11); + hws[IMX8MN_M7_ALT_PLL_OUT] = imx_clk_hw_gate("m7_alt_pll_out", "m7_alt_pll_bypass", base + 0x74, 11); + hws[IMX8MN_ARM_PLL_OUT] = imx_clk_hw_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11); + hws[IMX8MN_SYS_PLL3_OUT] = imx_clk_hw_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11); + + /* SYS PLL1 fixed output */ + hws[IMX8MN_SYS_PLL1_OUT] = imx_clk_hw_gate("sys_pll1_out", "sys_pll1", base + 0x94, 11); + hws[IMX8MN_SYS_PLL1_40M] = imx_clk_hw_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20); + hws[IMX8MN_SYS_PLL1_80M] = imx_clk_hw_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10); + hws[IMX8MN_SYS_PLL1_100M] = imx_clk_hw_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8); + hws[IMX8MN_SYS_PLL1_133M] = imx_clk_hw_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6); + hws[IMX8MN_SYS_PLL1_160M] = imx_clk_hw_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5); + hws[IMX8MN_SYS_PLL1_200M] = imx_clk_hw_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4); + hws[IMX8MN_SYS_PLL1_266M] = imx_clk_hw_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3); + hws[IMX8MN_SYS_PLL1_400M] = imx_clk_hw_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2); + hws[IMX8MN_SYS_PLL1_800M] = imx_clk_hw_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); + + /* SYS PLL2 fixed output */ + hws[IMX8MN_SYS_PLL2_OUT] = imx_clk_hw_gate("sys_pll2_out", "sys_pll2", base + 0x104, 11); + hws[IMX8MN_SYS_PLL2_50M] = imx_clk_hw_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20); + hws[IMX8MN_SYS_PLL2_100M] = imx_clk_hw_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10); + hws[IMX8MN_SYS_PLL2_125M] = imx_clk_hw_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8); + hws[IMX8MN_SYS_PLL2_166M] = imx_clk_hw_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6); + hws[IMX8MN_SYS_PLL2_200M] = imx_clk_hw_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5); + hws[IMX8MN_SYS_PLL2_250M] = imx_clk_hw_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4); + hws[IMX8MN_SYS_PLL2_333M] = imx_clk_hw_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3); + hws[IMX8MN_SYS_PLL2_500M] = imx_clk_hw_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2); + hws[IMX8MN_SYS_PLL2_1000M] = imx_clk_hw_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); + + hws[IMX8MN_CLK_CLKOUT1_SEL] = imx_clk_hw_mux2("clkout1_sel", base + 0x128, 4, 4, clkout_sels, ARRAY_SIZE(clkout_sels)); + hws[IMX8MN_CLK_CLKOUT1_DIV] = imx_clk_hw_divider("clkout1_div", "clkout1_sel", base + 0x128, 0, 4); + hws[IMX8MN_CLK_CLKOUT1] = imx_clk_hw_gate("clkout1", "clkout1_div", base + 0x128, 8); + hws[IMX8MN_CLK_CLKOUT2_SEL] = imx_clk_hw_mux2("clkout2_sel", base + 0x128, 20, 4, clkout_sels, ARRAY_SIZE(clkout_sels)); + hws[IMX8MN_CLK_CLKOUT2_DIV] = imx_clk_hw_divider("clkout2_div", "clkout2_sel", base + 0x128, 16, 4); + hws[IMX8MN_CLK_CLKOUT2] = imx_clk_hw_gate("clkout2", "clkout2_div", base + 0x128, 24); + + np = dev->of_node; + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) { + ret = PTR_ERR(base); + goto unregister_hws; + } + + /* CORE */ + hws[IMX8MN_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mn_a53_sels, base + 0x8000); + hws[IMX8MN_CLK_A53_SRC] = hws[IMX8MN_CLK_A53_DIV]; + hws[IMX8MN_CLK_A53_CG] = hws[IMX8MN_CLK_A53_DIV]; + + hws[IMX8MN_CLK_M7_CORE] = imx8m_clk_hw_composite_core("arm_m7_core", imx8mn_m7_sels, base + 0x8080); + + hws[IMX8MN_CLK_GPU_CORE] = imx8m_clk_hw_composite_core("gpu_core", imx8mn_gpu_core_sels, base + 0x8180); + hws[IMX8MN_CLK_GPU_SHADER] = imx8m_clk_hw_composite_core("gpu_shader", imx8mn_gpu_shader_sels, base + 0x8200); + + hws[IMX8MN_CLK_GPU_CORE_SRC] = hws[IMX8MN_CLK_GPU_CORE]; + hws[IMX8MN_CLK_GPU_CORE_CG] = hws[IMX8MN_CLK_GPU_CORE]; + hws[IMX8MN_CLK_GPU_CORE_DIV] = hws[IMX8MN_CLK_GPU_CORE]; + hws[IMX8MN_CLK_GPU_SHADER_SRC] = hws[IMX8MN_CLK_GPU_SHADER]; + hws[IMX8MN_CLK_GPU_SHADER_CG] = hws[IMX8MN_CLK_GPU_SHADER]; + hws[IMX8MN_CLK_GPU_SHADER_DIV] = hws[IMX8MN_CLK_GPU_SHADER]; + + /* CORE SEL */ + hws[IMX8MN_CLK_A53_CORE] = imx_clk_hw_mux2("arm_a53_core", base + 0x9880, 24, 1, imx8mn_a53_core_sels, ARRAY_SIZE(imx8mn_a53_core_sels)); + + /* BUS */ + hws[IMX8MN_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mn_main_axi_sels, base + 0x8800); + hws[IMX8MN_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mn_enet_axi_sels, base + 0x8880); + hws[IMX8MN_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus("nand_usdhc_bus", imx8mn_nand_usdhc_sels, base + 0x8900); + hws[IMX8MN_CLK_DISP_AXI] = imx8m_clk_hw_composite_bus("disp_axi", imx8mn_disp_axi_sels, base + 0x8a00); + hws[IMX8MN_CLK_DISP_APB] = imx8m_clk_hw_composite_bus("disp_apb", imx8mn_disp_apb_sels, base + 0x8a80); + hws[IMX8MN_CLK_USB_BUS] = imx8m_clk_hw_composite_bus("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80); + hws[IMX8MN_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mn_gpu_axi_sels, base + 0x8c00); + hws[IMX8MN_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mn_gpu_ahb_sels, base + 0x8c80); + hws[IMX8MN_CLK_NOC] = imx8m_clk_hw_composite_bus_critical("noc", imx8mn_noc_sels, base + 0x8d00); + + hws[IMX8MN_CLK_AHB] = imx8m_clk_hw_composite_bus_critical("ahb", imx8mn_ahb_sels, base + 0x9000); + hws[IMX8MN_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mn_audio_ahb_sels, base + 0x9100); + hws[IMX8MN_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb", base + 0x9080, 0, 1); + hws[IMX8MN_CLK_IPG_AUDIO_ROOT] = imx_clk_hw_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1); + hws[IMX8MN_CLK_DRAM_CORE] = imx_clk_hw_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mn_dram_core_sels, ARRAY_SIZE(imx8mn_dram_core_sels), CLK_IS_CRITICAL); + + /* + * DRAM clocks are manipulated from TF-A outside clock framework. + * The fw_managed helper sets GET_RATE_NOCACHE and clears SET_PARENT_GATE + * as div value should always be read from hardware + */ + hws[IMX8MN_CLK_DRAM_ALT] = imx8m_clk_hw_fw_managed_composite("dram_alt", imx8mn_dram_alt_sels, base + 0xa000); + hws[IMX8MN_CLK_DRAM_APB] = imx8m_clk_hw_fw_managed_composite_critical("dram_apb", imx8mn_dram_apb_sels, base + 0xa080); + + hws[IMX8MN_CLK_DISP_PIXEL] = imx8m_clk_hw_composite("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500); + hws[IMX8MN_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mn_sai2_sels, base + 0xa600); + hws[IMX8MN_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mn_sai3_sels, base + 0xa680); + hws[IMX8MN_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mn_sai5_sels, base + 0xa780); + hws[IMX8MN_CLK_SAI6] = imx8m_clk_hw_composite("sai6", imx8mn_sai6_sels, base + 0xa800); + hws[IMX8MN_CLK_SPDIF1] = imx8m_clk_hw_composite("spdif1", imx8mn_spdif1_sels, base + 0xa880); + hws[IMX8MN_CLK_ENET_REF] = imx8m_clk_hw_composite("enet_ref", imx8mn_enet_ref_sels, base + 0xa980); + hws[IMX8MN_CLK_ENET_TIMER] = imx8m_clk_hw_composite("enet_timer", imx8mn_enet_timer_sels, base + 0xaa00); + hws[IMX8MN_CLK_ENET_PHY_REF] = imx8m_clk_hw_composite("enet_phy", imx8mn_enet_phy_sels, base + 0xaa80); + hws[IMX8MN_CLK_NAND] = imx8m_clk_hw_composite("nand", imx8mn_nand_sels, base + 0xab00); + hws[IMX8MN_CLK_QSPI] = imx8m_clk_hw_composite("qspi", imx8mn_qspi_sels, base + 0xab80); + hws[IMX8MN_CLK_USDHC1] = imx8m_clk_hw_composite("usdhc1", imx8mn_usdhc1_sels, base + 0xac00); + hws[IMX8MN_CLK_USDHC2] = imx8m_clk_hw_composite("usdhc2", imx8mn_usdhc2_sels, base + 0xac80); + hws[IMX8MN_CLK_I2C1] = imx8m_clk_hw_composite("i2c1", imx8mn_i2c1_sels, base + 0xad00); + hws[IMX8MN_CLK_I2C2] = imx8m_clk_hw_composite("i2c2", imx8mn_i2c2_sels, base + 0xad80); + hws[IMX8MN_CLK_I2C3] = imx8m_clk_hw_composite("i2c3", imx8mn_i2c3_sels, base + 0xae00); + hws[IMX8MN_CLK_I2C4] = imx8m_clk_hw_composite("i2c4", imx8mn_i2c4_sels, base + 0xae80); + hws[IMX8MN_CLK_UART1] = imx8m_clk_hw_composite("uart1", imx8mn_uart1_sels, base + 0xaf00); + hws[IMX8MN_CLK_UART2] = imx8m_clk_hw_composite("uart2", imx8mn_uart2_sels, base + 0xaf80); + hws[IMX8MN_CLK_UART3] = imx8m_clk_hw_composite("uart3", imx8mn_uart3_sels, base + 0xb000); + hws[IMX8MN_CLK_UART4] = imx8m_clk_hw_composite("uart4", imx8mn_uart4_sels, base + 0xb080); + hws[IMX8MN_CLK_USB_CORE_REF] = imx8m_clk_hw_composite("usb_core_ref", imx8mn_usb_core_sels, base + 0xb100); + hws[IMX8MN_CLK_USB_PHY_REF] = imx8m_clk_hw_composite("usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180); + hws[IMX8MN_CLK_GIC] = imx8m_clk_hw_composite_critical("gic", imx8mn_gic_sels, base + 0xb200); + hws[IMX8MN_CLK_ECSPI1] = imx8m_clk_hw_composite("ecspi1", imx8mn_ecspi1_sels, base + 0xb280); + hws[IMX8MN_CLK_ECSPI2] = imx8m_clk_hw_composite("ecspi2", imx8mn_ecspi2_sels, base + 0xb300); + hws[IMX8MN_CLK_PWM1] = imx8m_clk_hw_composite("pwm1", imx8mn_pwm1_sels, base + 0xb380); + hws[IMX8MN_CLK_PWM2] = imx8m_clk_hw_composite("pwm2", imx8mn_pwm2_sels, base + 0xb400); + hws[IMX8MN_CLK_PWM3] = imx8m_clk_hw_composite("pwm3", imx8mn_pwm3_sels, base + 0xb480); + hws[IMX8MN_CLK_PWM4] = imx8m_clk_hw_composite("pwm4", imx8mn_pwm4_sels, base + 0xb500); + hws[IMX8MN_CLK_GPT1] = imx8m_clk_hw_composite("gpt1", imx8mn_gpt1_sels, base + 0xb580); + hws[IMX8MN_CLK_GPT2] = imx8m_clk_hw_composite("gpt2", imx8mn_gpt2_sels, base + 0xb600); + hws[IMX8MN_CLK_GPT3] = imx8m_clk_hw_composite("gpt3", imx8mn_gpt3_sels, base + 0xb680); + hws[IMX8MN_CLK_GPT4] = imx8m_clk_hw_composite("gpt4", imx8mn_gpt4_sels, base + 0xb700); + hws[IMX8MN_CLK_GPT5] = imx8m_clk_hw_composite("gpt5", imx8mn_gpt5_sels, base + 0xb780); + hws[IMX8MN_CLK_GPT6] = imx8m_clk_hw_composite("gpt6", imx8mn_gpt6_sels, base + 0xb800); + hws[IMX8MN_CLK_WDOG] = imx8m_clk_hw_composite("wdog", imx8mn_wdog_sels, base + 0xb900); + hws[IMX8MN_CLK_WRCLK] = imx8m_clk_hw_composite("wrclk", imx8mn_wrclk_sels, base + 0xb980); + hws[IMX8MN_CLK_CLKO1] = imx8m_clk_hw_composite("clko1", imx8mn_clko1_sels, base + 0xba00); + hws[IMX8MN_CLK_CLKO2] = imx8m_clk_hw_composite("clko2", imx8mn_clko2_sels, base + 0xba80); + hws[IMX8MN_CLK_DSI_CORE] = imx8m_clk_hw_composite("dsi_core", imx8mn_dsi_core_sels, base + 0xbb00); + hws[IMX8MN_CLK_DSI_PHY_REF] = imx8m_clk_hw_composite("dsi_phy_ref", imx8mn_dsi_phy_sels, base + 0xbb80); + hws[IMX8MN_CLK_DSI_DBI] = imx8m_clk_hw_composite("dsi_dbi", imx8mn_dsi_dbi_sels, base + 0xbc00); + hws[IMX8MN_CLK_USDHC3] = imx8m_clk_hw_composite("usdhc3", imx8mn_usdhc3_sels, base + 0xbc80); + hws[IMX8MN_CLK_CAMERA_PIXEL] = imx8m_clk_hw_composite("camera_pixel", imx8mn_camera_pixel_sels, base + 0xbd00); + hws[IMX8MN_CLK_CSI1_PHY_REF] = imx8m_clk_hw_composite("csi1_phy_ref", imx8mn_csi1_phy_sels, base + 0xbd80); + hws[IMX8MN_CLK_CSI2_PHY_REF] = imx8m_clk_hw_composite("csi2_phy_ref", imx8mn_csi2_phy_sels, base + 0xbf00); + hws[IMX8MN_CLK_CSI2_ESC] = imx8m_clk_hw_composite("csi2_esc", imx8mn_csi2_esc_sels, base + 0xbf80); + hws[IMX8MN_CLK_ECSPI3] = imx8m_clk_hw_composite("ecspi3", imx8mn_ecspi3_sels, base + 0xc180); + hws[IMX8MN_CLK_PDM] = imx8m_clk_hw_composite("pdm", imx8mn_pdm_sels, base + 0xc200); + hws[IMX8MN_CLK_SAI7] = imx8m_clk_hw_composite("sai7", imx8mn_sai7_sels, base + 0xc300); + + hws[IMX8MN_CLK_ECSPI1_ROOT] = imx_clk_hw_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0); + hws[IMX8MN_CLK_ECSPI2_ROOT] = imx_clk_hw_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0); + hws[IMX8MN_CLK_ECSPI3_ROOT] = imx_clk_hw_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0); + hws[IMX8MN_CLK_ENET1_ROOT] = imx_clk_hw_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0); + hws[IMX8MN_CLK_GPIO1_ROOT] = imx_clk_hw_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0); + hws[IMX8MN_CLK_GPIO2_ROOT] = imx_clk_hw_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0); + hws[IMX8MN_CLK_GPIO3_ROOT] = imx_clk_hw_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0); + hws[IMX8MN_CLK_GPIO4_ROOT] = imx_clk_hw_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0); + hws[IMX8MN_CLK_GPIO5_ROOT] = imx_clk_hw_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0); + hws[IMX8MN_CLK_GPT1_ROOT] = imx_clk_hw_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0); + hws[IMX8MN_CLK_GPT2_ROOT] = imx_clk_hw_gate4("gpt2_root_clk", "gpt2", base + 0x4110, 0); + hws[IMX8MN_CLK_GPT3_ROOT] = imx_clk_hw_gate4("gpt3_root_clk", "gpt3", base + 0x4120, 0); + hws[IMX8MN_CLK_GPT4_ROOT] = imx_clk_hw_gate4("gpt4_root_clk", "gpt4", base + 0x4130, 0); + hws[IMX8MN_CLK_GPT5_ROOT] = imx_clk_hw_gate4("gpt5_root_clk", "gpt5", base + 0x4140, 0); + hws[IMX8MN_CLK_GPT6_ROOT] = imx_clk_hw_gate4("gpt6_root_clk", "gpt6", base + 0x4150, 0); + hws[IMX8MN_CLK_I2C1_ROOT] = imx_clk_hw_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0); + hws[IMX8MN_CLK_I2C2_ROOT] = imx_clk_hw_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0); + hws[IMX8MN_CLK_I2C3_ROOT] = imx_clk_hw_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0); + hws[IMX8MN_CLK_I2C4_ROOT] = imx_clk_hw_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0); + hws[IMX8MN_CLK_MU_ROOT] = imx_clk_hw_gate4("mu_root_clk", "ipg_root", base + 0x4210, 0); + hws[IMX8MN_CLK_OCOTP_ROOT] = imx_clk_hw_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0); + hws[IMX8MN_CLK_PWM1_ROOT] = imx_clk_hw_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0); + hws[IMX8MN_CLK_PWM2_ROOT] = imx_clk_hw_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0); + hws[IMX8MN_CLK_PWM3_ROOT] = imx_clk_hw_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0); + hws[IMX8MN_CLK_PWM4_ROOT] = imx_clk_hw_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0); + hws[IMX8MN_CLK_QSPI_ROOT] = imx_clk_hw_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0); + hws[IMX8MN_CLK_NAND_ROOT] = imx_clk_hw_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand); + hws[IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand); + hws[IMX8MN_CLK_SAI2_ROOT] = imx_clk_hw_gate2_shared2("sai2_root_clk", "sai2", base + 0x4340, 0, &share_count_sai2); + hws[IMX8MN_CLK_SAI2_IPG] = imx_clk_hw_gate2_shared2("sai2_ipg_clk", "ipg_audio_root", base + 0x4340, 0, &share_count_sai2); + hws[IMX8MN_CLK_SAI3_ROOT] = imx_clk_hw_gate2_shared2("sai3_root_clk", "sai3", base + 0x4350, 0, &share_count_sai3); + hws[IMX8MN_CLK_SAI3_IPG] = imx_clk_hw_gate2_shared2("sai3_ipg_clk", "ipg_audio_root", base + 0x4350, 0, &share_count_sai3); + hws[IMX8MN_CLK_SAI5_ROOT] = imx_clk_hw_gate2_shared2("sai5_root_clk", "sai5", base + 0x4370, 0, &share_count_sai5); + hws[IMX8MN_CLK_SAI5_IPG] = imx_clk_hw_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", base + 0x4370, 0, &share_count_sai5); + hws[IMX8MN_CLK_SAI6_ROOT] = imx_clk_hw_gate2_shared2("sai6_root_clk", "sai6", base + 0x4380, 0, &share_count_sai6); + hws[IMX8MN_CLK_SAI6_IPG] = imx_clk_hw_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", base + 0x4380, 0, &share_count_sai6); + hws[IMX8MN_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", base + 0x4490, 0); + hws[IMX8MN_CLK_UART2_ROOT] = imx_clk_hw_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0); + hws[IMX8MN_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); + hws[IMX8MN_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); + hws[IMX8MN_CLK_USB1_CTRL_ROOT] = imx_clk_hw_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0); + hws[IMX8MN_CLK_GPU_CORE_ROOT] = imx_clk_hw_gate4("gpu_core_root_clk", "gpu_core", base + 0x44f0, 0); + hws[IMX8MN_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); + hws[IMX8MN_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); + hws[IMX8MN_CLK_WDOG1_ROOT] = imx_clk_hw_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0); + hws[IMX8MN_CLK_WDOG2_ROOT] = imx_clk_hw_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0); + hws[IMX8MN_CLK_WDOG3_ROOT] = imx_clk_hw_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0); + hws[IMX8MN_CLK_GPU_BUS_ROOT] = imx_clk_hw_gate4("gpu_root_clk", "gpu_axi", base + 0x4570, 0); + hws[IMX8MN_CLK_ASRC_ROOT] = imx_clk_hw_gate4("asrc_root_clk", "audio_ahb", base + 0x4580, 0); + hws[IMX8MN_CLK_PDM_ROOT] = imx_clk_hw_gate2_shared2("pdm_root_clk", "pdm", base + 0x45b0, 0, &share_count_pdm); + hws[IMX8MN_CLK_PDM_IPG] = imx_clk_hw_gate2_shared2("pdm_ipg_clk", "ipg_audio_root", base + 0x45b0, 0, &share_count_pdm); + hws[IMX8MN_CLK_DISP_AXI_ROOT] = imx_clk_hw_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MN_CLK_DISP_APB_ROOT] = imx_clk_hw_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MN_CLK_CAMERA_PIXEL_ROOT] = imx_clk_hw_gate2_shared2("camera_pixel_clk", "camera_pixel", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MN_CLK_DISP_PIXEL_ROOT] = imx_clk_hw_gate2_shared2("disp_pixel_clk", "disp_pixel", base + 0x45d0, 0, &share_count_disp); + hws[IMX8MN_CLK_USDHC3_ROOT] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0); + hws[IMX8MN_CLK_TMU_ROOT] = imx_clk_hw_gate4("tmu_root_clk", "ipg_root", base + 0x4620, 0); + hws[IMX8MN_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0); + hws[IMX8MN_CLK_SDMA2_ROOT] = imx_clk_hw_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0); + hws[IMX8MN_CLK_SDMA3_ROOT] = imx_clk_hw_gate4("sdma3_clk", "ipg_audio_root", base + 0x45f0, 0); + hws[IMX8MN_CLK_SAI7_ROOT] = imx_clk_hw_gate2_shared2("sai7_root_clk", "sai7", base + 0x4650, 0, &share_count_sai7); + + hws[IMX8MN_CLK_GPT_3M] = imx_clk_hw_fixed_factor("gpt_3m", "osc_24m", 1, 8); + + hws[IMX8MN_CLK_DRAM_ALT_ROOT] = imx_clk_hw_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + + hws[IMX8MN_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MN_CLK_A53_CORE]->clk, + hws[IMX8MN_CLK_A53_CORE]->clk, + hws[IMX8MN_ARM_PLL_OUT]->clk, + hws[IMX8MN_CLK_A53_DIV]->clk); + + imx_check_clk_hws(hws, IMX8MN_CLK_END); + + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + if (ret < 0) { + dev_err(dev, "failed to register hws for i.MX8MN\n"); + goto unregister_hws; + } + + imx_register_uart_clocks(4); + + return 0; + +unregister_hws: + imx_unregister_hw_clocks(hws, IMX8MN_CLK_END); + + return ret; +} + +static const struct of_device_id imx8mn_clk_of_match[] = { + { .compatible = "fsl,imx8mn-ccm" }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx8mn_clk_of_match); + +static struct platform_driver imx8mn_clk_driver = { + .probe = imx8mn_clocks_probe, + .driver = { + .name = "imx8mn-ccm", + /* + * Disable bind attributes: clocks are not removed and + * reloading the driver will crash or break devices. + */ + .suppress_bind_attrs = true, + .of_match_table = imx8mn_clk_of_match, + }, +}; +module_platform_driver(imx8mn_clk_driver); +module_param(mcore_booted, bool, S_IRUGO); +MODULE_PARM_DESC(mcore_booted, "See Cortex-M core is booted or not"); + +MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); +MODULE_DESCRIPTION("NXP i.MX8MN clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c new file mode 100644 index 000000000..3d0d8f2c0 --- /dev/null +++ b/drivers/clk/imx/clk-imx8mp.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <dt-bindings/clock/imx8mp-clock.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "clk.h" + +static u32 share_count_nand; +static u32 share_count_media; +static u32 share_count_usb; + +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; +static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; +static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; +static const char * const sys_pll2_bypass_sels[] = {"sys_pll2", "sys_pll2_ref_sel", }; +static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; + +static const char * const imx8mp_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", + "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", + "audio_pll1_out", "sys_pll3_out", }; + +static const char * const imx8mp_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + +static const char * const imx8mp_m7_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", + "vpu_pll_out", "sys_pll1_800m", "audio_pll1_out", + "video_pll1_out", "sys_pll3_out", }; + +static const char * const imx8mp_ml_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_gpu3d_core_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_gpu3d_shader_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_gpu2d_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_audio_axi_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_hsio_axi_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mp_media_isp_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_400m", "audio_pll2_out", + "clk_ext1", "sys_pll2_500m", }; + +static const char * const imx8mp_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", + "sys_pll2_250m", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "sys_pll1_100m",}; + +static const char * const imx8mp_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", + "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", + "video_pll1_out", "sys_pll3_out", }; + +static const char * const imx8mp_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", + "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll1_out", }; + +static const char * const imx8mp_vpu_bus_sels[] = {"osc_24m", "sys_pll1_800m", "vpu_pll_out", + "audio_pll2_out", "sys_pll3_out", "sys_pll2_1000m", + "sys_pll2_200m", "sys_pll1_100m", }; + +static const char * const imx8mp_media_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "sys_pll2_500m", }; + +static const char * const imx8mp_media_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "sys_pll1_133m", }; + +static const char * const imx8mp_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_noc_io_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_ml_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_ml_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", + "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", + "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mp_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll2_166m", "sys_pll3_out", + "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mp_mipi_dsi_esc_rx_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", + "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll3_out", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mp_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", + "sys_pll2_500m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll1_out", "sys_pll1_266m", }; + +static const char * const imx8mp_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mp_vpu_g1_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll1_100m", "sys_pll2_125m", + "sys_pll3_out", "audio_pll1_out", }; + +static const char * const imx8mp_vpu_g2_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll1_100m", "sys_pll2_125m", + "sys_pll3_out", "audio_pll1_out", }; + +static const char * const imx8mp_can1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mp_can2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mp_pcie_aux_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", + "sys_pll3_out", "sys_pll2_100m", "sys_pll1_80m", + "sys_pll1_160m", "sys_pll1_200m", }; + +static const char * const imx8mp_i2c5_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mp_i2c6_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mp_sai1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext1", "clk_ext2", }; + +static const char * const imx8mp_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext2", "clk_ext3", }; + +static const char * const imx8mp_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mp_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext2", "clk_ext3", }; + +static const char * const imx8mp_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mp_enet_qos_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", + "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", + "video_pll1_out", "clk_ext4", }; + +static const char * const imx8mp_enet_qos_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", + "clk_ext1", "clk_ext2", "clk_ext3", + "clk_ext4", "video_pll1_out", }; + +static const char * const imx8mp_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", + "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", + "video_pll1_out", "clk_ext4", }; + +static const char * const imx8mp_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", + "clk_ext1", "clk_ext2", "clk_ext3", + "clk_ext4", "video_pll1_out", }; + +static const char * const imx8mp_enet_phy_ref_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", + "sys_pll2_200m", "sys_pll2_500m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mp_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", + "sys_pll1_400m", "audio_pll2_out", "sys_pll3_out", + "sys_pll2_250m", "video_pll1_out", }; + +static const char * const imx8mp_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", + "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", + "sys_pll3_out", "sys_pll1_100m", }; + +static const char * const imx8mp_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mp_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mp_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mp_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mp_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mp_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mp_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mp_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mp_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mp_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mp_usb_core_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mp_usb_phy_ref_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mp_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll1_800m", + "sys_pll2_500m", "clk_ext4", "audio_pll2_out" }; + +static const char * const imx8mp_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mp_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mp_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext1", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mp_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext1", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mp_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext2", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mp_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext2", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mp_gpt1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1" }; + +static const char * const imx8mp_gpt2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext2" }; + +static const char * const imx8mp_gpt3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext3" }; + +static const char * const imx8mp_gpt4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext1" }; + +static const char * const imx8mp_gpt5_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext2" }; + +static const char * const imx8mp_gpt6_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_400m", + "sys_pll1_40m", "video_pll1_out", "sys_pll1_80m", + "audio_pll1_out", "clk_ext3" }; + +static const char * const imx8mp_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", + "vpu_pll_out", "sys_pll2_125m", "sys_pll3_out", + "sys_pll1_80m", "sys_pll2_166m" }; + +static const char * const imx8mp_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "vpu_pll_out", + "sys_pll3_out", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll2_500m", "sys_pll1_100m" }; + +static const char * const imx8mp_ipp_do_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_133m", + "sys_pll1_200m", "audio_pll2_out", "sys_pll2_500m", + "vpu_pll_out", "sys_pll1_80m" }; + +static const char * const imx8mp_ipp_do_clko2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_400m", + "sys_pll1_166m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "osc_32k" }; + +static const char * const imx8mp_hdmi_fdcc_tst_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mp_hdmi_24m_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mp_hdmi_ref_266m_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll3_out", + "sys_pll2_333m", "sys_pll1_266m", "sys_pll2_200m", + "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mp_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mp_media_cam1_pix_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", + "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll3_out", "audio_pll2_out", + "video_pll1_out", }; + +static const char * const imx8mp_media_mipi_phy1_ref_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", + "clk_ext2", "audio_pll2_out", + "video_pll1_out", }; + +static const char * const imx8mp_media_disp_pix_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", + "audio_pll1_out", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll3_out", "clk_ext4", }; + +static const char * const imx8mp_media_cam2_pix_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", + "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll3_out", "audio_pll2_out", + "video_pll1_out", }; + +static const char * const imx8mp_media_ldb_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", + "clk_ext2", "audio_pll2_out", + "video_pll1_out", }; + +static const char * const imx8mp_memrepair_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mp_media_mipi_test_byte_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_50m", + "sys_pll3_out", "sys_pll2_100m", + "sys_pll1_80m", "sys_pll1_160m", + "sys_pll1_200m", }; + +static const char * const imx8mp_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mp_pdm_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mp_vpu_vc8000e_sels[] = {"osc_24m", "vpu_pll_out", "sys_pll1_800m", + "sys_pll2_1000m", "audio_pll2_out", "sys_pll2_125m", + "sys_pll3_out", "audio_pll1_out", }; + +static const char * const imx8mp_sai7_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mp_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; + +static const char * const imx8mp_clkout_sels[] = {"audio_pll1_out", "audio_pll2_out", "video_pll1_out", + "dummy", "dummy", "gpu_pll_out", "vpu_pll_out", + "arm_pll_out", "sys_pll1", "sys_pll2", "sys_pll3", + "dummy", "dummy", "osc_24m", "dummy", "osc_32k"}; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; + +static int imx8mp_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np; + void __iomem *anatop_base, *ccm_base; + int err; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mp-anatop"); + anatop_base = devm_of_iomap(dev, np, 0, NULL); + of_node_put(np); + if (WARN_ON(IS_ERR(anatop_base))) + return PTR_ERR(anatop_base); + + np = dev->of_node; + ccm_base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(ccm_base))) + return PTR_ERR(ccm_base); + + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMX8MP_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return -ENOMEM; + + clk_hw_data->num = IMX8MP_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX8MP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + hws[IMX8MP_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m"); + hws[IMX8MP_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k"); + hws[IMX8MP_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1"); + hws[IMX8MP_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2"); + hws[IMX8MP_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3"); + hws[IMX8MP_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4"); + + hws[IMX8MP_AUDIO_PLL1_REF_SEL] = imx_clk_hw_mux("audio_pll1_ref_sel", anatop_base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_AUDIO_PLL2_REF_SEL] = imx_clk_hw_mux("audio_pll2_ref_sel", anatop_base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_VIDEO_PLL1_REF_SEL] = imx_clk_hw_mux("video_pll1_ref_sel", anatop_base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_DRAM_PLL_REF_SEL] = imx_clk_hw_mux("dram_pll_ref_sel", anatop_base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_GPU_PLL_REF_SEL] = imx_clk_hw_mux("gpu_pll_ref_sel", anatop_base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_VPU_PLL_REF_SEL] = imx_clk_hw_mux("vpu_pll_ref_sel", anatop_base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_ARM_PLL_REF_SEL] = imx_clk_hw_mux("arm_pll_ref_sel", anatop_base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_SYS_PLL1_REF_SEL] = imx_clk_hw_mux("sys_pll1_ref_sel", anatop_base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_SYS_PLL2_REF_SEL] = imx_clk_hw_mux("sys_pll2_ref_sel", anatop_base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MP_SYS_PLL3_REF_SEL] = imx_clk_hw_mux("sys_pll3_ref_sel", anatop_base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + + hws[IMX8MP_AUDIO_PLL1] = imx_clk_hw_pll14xx("audio_pll1", "audio_pll1_ref_sel", anatop_base, &imx_1443x_pll); + hws[IMX8MP_AUDIO_PLL2] = imx_clk_hw_pll14xx("audio_pll2", "audio_pll2_ref_sel", anatop_base + 0x14, &imx_1443x_pll); + hws[IMX8MP_VIDEO_PLL1] = imx_clk_hw_pll14xx("video_pll1", "video_pll1_ref_sel", anatop_base + 0x28, &imx_1443x_pll); + hws[IMX8MP_DRAM_PLL] = imx_clk_hw_pll14xx("dram_pll", "dram_pll_ref_sel", anatop_base + 0x50, &imx_1443x_dram_pll); + hws[IMX8MP_GPU_PLL] = imx_clk_hw_pll14xx("gpu_pll", "gpu_pll_ref_sel", anatop_base + 0x64, &imx_1416x_pll); + hws[IMX8MP_VPU_PLL] = imx_clk_hw_pll14xx("vpu_pll", "vpu_pll_ref_sel", anatop_base + 0x74, &imx_1416x_pll); + hws[IMX8MP_ARM_PLL] = imx_clk_hw_pll14xx("arm_pll", "arm_pll_ref_sel", anatop_base + 0x84, &imx_1416x_pll); + hws[IMX8MP_SYS_PLL1] = imx_clk_hw_pll14xx("sys_pll1", "sys_pll1_ref_sel", anatop_base + 0x94, &imx_1416x_pll); + hws[IMX8MP_SYS_PLL2] = imx_clk_hw_pll14xx("sys_pll2", "sys_pll2_ref_sel", anatop_base + 0x104, &imx_1416x_pll); + hws[IMX8MP_SYS_PLL3] = imx_clk_hw_pll14xx("sys_pll3", "sys_pll3_ref_sel", anatop_base + 0x114, &imx_1416x_pll); + + hws[IMX8MP_AUDIO_PLL1_BYPASS] = imx_clk_hw_mux_flags("audio_pll1_bypass", anatop_base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_AUDIO_PLL2_BYPASS] = imx_clk_hw_mux_flags("audio_pll2_bypass", anatop_base + 0x14, 16, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_VIDEO_PLL1_BYPASS] = imx_clk_hw_mux_flags("video_pll1_bypass", anatop_base + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_DRAM_PLL_BYPASS] = imx_clk_hw_mux_flags("dram_pll_bypass", anatop_base + 0x50, 16, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_GPU_PLL_BYPASS] = imx_clk_hw_mux_flags("gpu_pll_bypass", anatop_base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_VPU_PLL_BYPASS] = imx_clk_hw_mux_flags("vpu_pll_bypass", anatop_base + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_ARM_PLL_BYPASS] = imx_clk_hw_mux_flags("arm_pll_bypass", anatop_base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_SYS_PLL1_BYPASS] = imx_clk_hw_mux_flags("sys_pll1_bypass", anatop_base + 0x94, 28, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_SYS_PLL2_BYPASS] = imx_clk_hw_mux_flags("sys_pll2_bypass", anatop_base + 0x104, 28, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MP_SYS_PLL3_BYPASS] = imx_clk_hw_mux_flags("sys_pll3_bypass", anatop_base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); + + hws[IMX8MP_AUDIO_PLL1_OUT] = imx_clk_hw_gate("audio_pll1_out", "audio_pll1_bypass", anatop_base, 13); + hws[IMX8MP_AUDIO_PLL2_OUT] = imx_clk_hw_gate("audio_pll2_out", "audio_pll2_bypass", anatop_base + 0x14, 13); + hws[IMX8MP_VIDEO_PLL1_OUT] = imx_clk_hw_gate("video_pll1_out", "video_pll1_bypass", anatop_base + 0x28, 13); + hws[IMX8MP_DRAM_PLL_OUT] = imx_clk_hw_gate("dram_pll_out", "dram_pll_bypass", anatop_base + 0x50, 13); + hws[IMX8MP_GPU_PLL_OUT] = imx_clk_hw_gate("gpu_pll_out", "gpu_pll_bypass", anatop_base + 0x64, 11); + hws[IMX8MP_VPU_PLL_OUT] = imx_clk_hw_gate("vpu_pll_out", "vpu_pll_bypass", anatop_base + 0x74, 11); + hws[IMX8MP_ARM_PLL_OUT] = imx_clk_hw_gate("arm_pll_out", "arm_pll_bypass", anatop_base + 0x84, 11); + hws[IMX8MP_SYS_PLL3_OUT] = imx_clk_hw_gate("sys_pll3_out", "sys_pll3_bypass", anatop_base + 0x114, 11); + + hws[IMX8MP_SYS_PLL1_OUT] = imx_clk_hw_gate("sys_pll1_out", "sys_pll1_bypass", anatop_base + 0x94, 11); + + hws[IMX8MP_SYS_PLL1_40M] = imx_clk_hw_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20); + hws[IMX8MP_SYS_PLL1_80M] = imx_clk_hw_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10); + hws[IMX8MP_SYS_PLL1_100M] = imx_clk_hw_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8); + hws[IMX8MP_SYS_PLL1_133M] = imx_clk_hw_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6); + hws[IMX8MP_SYS_PLL1_160M] = imx_clk_hw_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5); + hws[IMX8MP_SYS_PLL1_200M] = imx_clk_hw_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4); + hws[IMX8MP_SYS_PLL1_266M] = imx_clk_hw_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3); + hws[IMX8MP_SYS_PLL1_400M] = imx_clk_hw_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2); + hws[IMX8MP_SYS_PLL1_800M] = imx_clk_hw_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); + + hws[IMX8MP_SYS_PLL2_OUT] = imx_clk_hw_gate("sys_pll2_out", "sys_pll2_bypass", anatop_base + 0x104, 11); + + hws[IMX8MP_SYS_PLL2_50M] = imx_clk_hw_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20); + hws[IMX8MP_SYS_PLL2_100M] = imx_clk_hw_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10); + hws[IMX8MP_SYS_PLL2_125M] = imx_clk_hw_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8); + hws[IMX8MP_SYS_PLL2_166M] = imx_clk_hw_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6); + hws[IMX8MP_SYS_PLL2_200M] = imx_clk_hw_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5); + hws[IMX8MP_SYS_PLL2_250M] = imx_clk_hw_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4); + hws[IMX8MP_SYS_PLL2_333M] = imx_clk_hw_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3); + hws[IMX8MP_SYS_PLL2_500M] = imx_clk_hw_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2); + hws[IMX8MP_SYS_PLL2_1000M] = imx_clk_hw_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); + + hws[IMX8MP_CLK_CLKOUT1_SEL] = imx_clk_hw_mux2("clkout1_sel", anatop_base + 0x128, 4, 4, + imx8mp_clkout_sels, ARRAY_SIZE(imx8mp_clkout_sels)); + hws[IMX8MP_CLK_CLKOUT1_DIV] = imx_clk_hw_divider("clkout1_div", "clkout1_sel", anatop_base + 0x128, 0, 4); + hws[IMX8MP_CLK_CLKOUT1] = imx_clk_hw_gate("clkout1", "clkout1_div", anatop_base + 0x128, 8); + hws[IMX8MP_CLK_CLKOUT2_SEL] = imx_clk_hw_mux2("clkout2_sel", anatop_base + 0x128, 20, 4, + imx8mp_clkout_sels, ARRAY_SIZE(imx8mp_clkout_sels)); + hws[IMX8MP_CLK_CLKOUT2_DIV] = imx_clk_hw_divider("clkout2_div", "clkout2_sel", anatop_base + 0x128, 16, 4); + hws[IMX8MP_CLK_CLKOUT2] = imx_clk_hw_gate("clkout2", "clkout2_div", anatop_base + 0x128, 24); + + hws[IMX8MP_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mp_a53_sels, ccm_base + 0x8000); + hws[IMX8MP_CLK_A53_SRC] = hws[IMX8MP_CLK_A53_DIV]; + hws[IMX8MP_CLK_A53_CG] = hws[IMX8MP_CLK_A53_DIV]; + hws[IMX8MP_CLK_M7_CORE] = imx8m_clk_hw_composite_core("m7_core", imx8mp_m7_sels, ccm_base + 0x8080); + hws[IMX8MP_CLK_ML_CORE] = imx8m_clk_hw_composite_core("ml_core", imx8mp_ml_sels, ccm_base + 0x8100); + hws[IMX8MP_CLK_GPU3D_CORE] = imx8m_clk_hw_composite_core("gpu3d_core", imx8mp_gpu3d_core_sels, ccm_base + 0x8180); + hws[IMX8MP_CLK_GPU3D_SHADER_CORE] = imx8m_clk_hw_composite("gpu3d_shader_core", imx8mp_gpu3d_shader_sels, ccm_base + 0x8200); + hws[IMX8MP_CLK_GPU2D_CORE] = imx8m_clk_hw_composite("gpu2d_core", imx8mp_gpu2d_sels, ccm_base + 0x8280); + hws[IMX8MP_CLK_AUDIO_AXI] = imx8m_clk_hw_composite("audio_axi", imx8mp_audio_axi_sels, ccm_base + 0x8300); + hws[IMX8MP_CLK_AUDIO_AXI_SRC] = hws[IMX8MP_CLK_AUDIO_AXI]; + hws[IMX8MP_CLK_HSIO_AXI] = imx8m_clk_hw_composite("hsio_axi", imx8mp_hsio_axi_sels, ccm_base + 0x8380); + hws[IMX8MP_CLK_MEDIA_ISP] = imx8m_clk_hw_composite("media_isp", imx8mp_media_isp_sels, ccm_base + 0x8400); + + /* CORE SEL */ + hws[IMX8MP_CLK_A53_CORE] = imx_clk_hw_mux2("arm_a53_core", ccm_base + 0x9880, 24, 1, imx8mp_a53_core_sels, ARRAY_SIZE(imx8mp_a53_core_sels)); + + hws[IMX8MP_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mp_main_axi_sels, ccm_base + 0x8800); + hws[IMX8MP_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mp_enet_axi_sels, ccm_base + 0x8880); + hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900); + hws[IMX8MP_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mp_vpu_bus_sels, ccm_base + 0x8980); + hws[IMX8MP_CLK_MEDIA_AXI] = imx8m_clk_hw_composite_bus("media_axi", imx8mp_media_axi_sels, ccm_base + 0x8a00); + hws[IMX8MP_CLK_MEDIA_APB] = imx8m_clk_hw_composite_bus("media_apb", imx8mp_media_apb_sels, ccm_base + 0x8a80); + hws[IMX8MP_CLK_HDMI_APB] = imx8m_clk_hw_composite_bus("hdmi_apb", imx8mp_media_apb_sels, ccm_base + 0x8b00); + hws[IMX8MP_CLK_HDMI_AXI] = imx8m_clk_hw_composite_bus("hdmi_axi", imx8mp_media_axi_sels, ccm_base + 0x8b80); + hws[IMX8MP_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mp_gpu_axi_sels, ccm_base + 0x8c00); + hws[IMX8MP_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mp_gpu_ahb_sels, ccm_base + 0x8c80); + hws[IMX8MP_CLK_NOC] = imx8m_clk_hw_composite_bus_critical("noc", imx8mp_noc_sels, ccm_base + 0x8d00); + hws[IMX8MP_CLK_NOC_IO] = imx8m_clk_hw_composite_bus_critical("noc_io", imx8mp_noc_io_sels, ccm_base + 0x8d80); + hws[IMX8MP_CLK_ML_AXI] = imx8m_clk_hw_composite_bus("ml_axi", imx8mp_ml_axi_sels, ccm_base + 0x8e00); + hws[IMX8MP_CLK_ML_AHB] = imx8m_clk_hw_composite_bus("ml_ahb", imx8mp_ml_ahb_sels, ccm_base + 0x8e80); + + hws[IMX8MP_CLK_AHB] = imx8m_clk_hw_composite_bus_critical("ahb_root", imx8mp_ahb_sels, ccm_base + 0x9000); + hws[IMX8MP_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mp_audio_ahb_sels, ccm_base + 0x9100); + hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite_bus("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200); + hws[IMX8MP_CLK_MEDIA_DISP2_PIX] = imx8m_clk_hw_composite("media_disp2_pix", imx8mp_media_disp_pix_sels, ccm_base + 0x9300); + + hws[IMX8MP_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb_root", ccm_base + 0x9080, 0, 1); + + hws[IMX8MP_CLK_DRAM_ALT] = imx8m_clk_hw_composite("dram_alt", imx8mp_dram_alt_sels, ccm_base + 0xa000); + hws[IMX8MP_CLK_DRAM_APB] = imx8m_clk_hw_composite_critical("dram_apb", imx8mp_dram_apb_sels, ccm_base + 0xa080); + hws[IMX8MP_CLK_VPU_G1] = imx8m_clk_hw_composite("vpu_g1", imx8mp_vpu_g1_sels, ccm_base + 0xa100); + hws[IMX8MP_CLK_VPU_G2] = imx8m_clk_hw_composite("vpu_g2", imx8mp_vpu_g2_sels, ccm_base + 0xa180); + hws[IMX8MP_CLK_CAN1] = imx8m_clk_hw_composite("can1", imx8mp_can1_sels, ccm_base + 0xa200); + hws[IMX8MP_CLK_CAN2] = imx8m_clk_hw_composite("can2", imx8mp_can2_sels, ccm_base + 0xa280); + hws[IMX8MP_CLK_PCIE_AUX] = imx8m_clk_hw_composite("pcie_aux", imx8mp_pcie_aux_sels, ccm_base + 0xa400); + hws[IMX8MP_CLK_I2C5] = imx8m_clk_hw_composite("i2c5", imx8mp_i2c5_sels, ccm_base + 0xa480); + hws[IMX8MP_CLK_I2C6] = imx8m_clk_hw_composite("i2c6", imx8mp_i2c6_sels, ccm_base + 0xa500); + hws[IMX8MP_CLK_SAI1] = imx8m_clk_hw_composite("sai1", imx8mp_sai1_sels, ccm_base + 0xa580); + hws[IMX8MP_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mp_sai2_sels, ccm_base + 0xa600); + hws[IMX8MP_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mp_sai3_sels, ccm_base + 0xa680); + hws[IMX8MP_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mp_sai5_sels, ccm_base + 0xa780); + hws[IMX8MP_CLK_SAI6] = imx8m_clk_hw_composite("sai6", imx8mp_sai6_sels, ccm_base + 0xa800); + hws[IMX8MP_CLK_ENET_QOS] = imx8m_clk_hw_composite("enet_qos", imx8mp_enet_qos_sels, ccm_base + 0xa880); + hws[IMX8MP_CLK_ENET_QOS_TIMER] = imx8m_clk_hw_composite("enet_qos_timer", imx8mp_enet_qos_timer_sels, ccm_base + 0xa900); + hws[IMX8MP_CLK_ENET_REF] = imx8m_clk_hw_composite("enet_ref", imx8mp_enet_ref_sels, ccm_base + 0xa980); + hws[IMX8MP_CLK_ENET_TIMER] = imx8m_clk_hw_composite("enet_timer", imx8mp_enet_timer_sels, ccm_base + 0xaa00); + hws[IMX8MP_CLK_ENET_PHY_REF] = imx8m_clk_hw_composite("enet_phy_ref", imx8mp_enet_phy_ref_sels, ccm_base + 0xaa80); + hws[IMX8MP_CLK_NAND] = imx8m_clk_hw_composite("nand", imx8mp_nand_sels, ccm_base + 0xab00); + hws[IMX8MP_CLK_QSPI] = imx8m_clk_hw_composite("qspi", imx8mp_qspi_sels, ccm_base + 0xab80); + hws[IMX8MP_CLK_USDHC1] = imx8m_clk_hw_composite("usdhc1", imx8mp_usdhc1_sels, ccm_base + 0xac00); + hws[IMX8MP_CLK_USDHC2] = imx8m_clk_hw_composite("usdhc2", imx8mp_usdhc2_sels, ccm_base + 0xac80); + hws[IMX8MP_CLK_I2C1] = imx8m_clk_hw_composite("i2c1", imx8mp_i2c1_sels, ccm_base + 0xad00); + hws[IMX8MP_CLK_I2C2] = imx8m_clk_hw_composite("i2c2", imx8mp_i2c2_sels, ccm_base + 0xad80); + hws[IMX8MP_CLK_I2C3] = imx8m_clk_hw_composite("i2c3", imx8mp_i2c3_sels, ccm_base + 0xae00); + hws[IMX8MP_CLK_I2C4] = imx8m_clk_hw_composite("i2c4", imx8mp_i2c4_sels, ccm_base + 0xae80); + + hws[IMX8MP_CLK_UART1] = imx8m_clk_hw_composite("uart1", imx8mp_uart1_sels, ccm_base + 0xaf00); + hws[IMX8MP_CLK_UART2] = imx8m_clk_hw_composite("uart2", imx8mp_uart2_sels, ccm_base + 0xaf80); + hws[IMX8MP_CLK_UART3] = imx8m_clk_hw_composite("uart3", imx8mp_uart3_sels, ccm_base + 0xb000); + hws[IMX8MP_CLK_UART4] = imx8m_clk_hw_composite("uart4", imx8mp_uart4_sels, ccm_base + 0xb080); + hws[IMX8MP_CLK_USB_CORE_REF] = imx8m_clk_hw_composite("usb_core_ref", imx8mp_usb_core_ref_sels, ccm_base + 0xb100); + hws[IMX8MP_CLK_USB_PHY_REF] = imx8m_clk_hw_composite("usb_phy_ref", imx8mp_usb_phy_ref_sels, ccm_base + 0xb180); + hws[IMX8MP_CLK_GIC] = imx8m_clk_hw_composite_critical("gic", imx8mp_gic_sels, ccm_base + 0xb200); + hws[IMX8MP_CLK_ECSPI1] = imx8m_clk_hw_composite("ecspi1", imx8mp_ecspi1_sels, ccm_base + 0xb280); + hws[IMX8MP_CLK_ECSPI2] = imx8m_clk_hw_composite("ecspi2", imx8mp_ecspi2_sels, ccm_base + 0xb300); + hws[IMX8MP_CLK_PWM1] = imx8m_clk_hw_composite("pwm1", imx8mp_pwm1_sels, ccm_base + 0xb380); + hws[IMX8MP_CLK_PWM2] = imx8m_clk_hw_composite("pwm2", imx8mp_pwm2_sels, ccm_base + 0xb400); + hws[IMX8MP_CLK_PWM3] = imx8m_clk_hw_composite("pwm3", imx8mp_pwm3_sels, ccm_base + 0xb480); + hws[IMX8MP_CLK_PWM4] = imx8m_clk_hw_composite("pwm4", imx8mp_pwm4_sels, ccm_base + 0xb500); + + hws[IMX8MP_CLK_GPT1] = imx8m_clk_hw_composite("gpt1", imx8mp_gpt1_sels, ccm_base + 0xb580); + hws[IMX8MP_CLK_GPT2] = imx8m_clk_hw_composite("gpt2", imx8mp_gpt2_sels, ccm_base + 0xb600); + hws[IMX8MP_CLK_GPT3] = imx8m_clk_hw_composite("gpt3", imx8mp_gpt3_sels, ccm_base + 0xb680); + hws[IMX8MP_CLK_GPT4] = imx8m_clk_hw_composite("gpt4", imx8mp_gpt4_sels, ccm_base + 0xb700); + hws[IMX8MP_CLK_GPT5] = imx8m_clk_hw_composite("gpt5", imx8mp_gpt5_sels, ccm_base + 0xb780); + hws[IMX8MP_CLK_GPT6] = imx8m_clk_hw_composite("gpt6", imx8mp_gpt6_sels, ccm_base + 0xb800); + hws[IMX8MP_CLK_WDOG] = imx8m_clk_hw_composite("wdog", imx8mp_wdog_sels, ccm_base + 0xb900); + hws[IMX8MP_CLK_WRCLK] = imx8m_clk_hw_composite("wrclk", imx8mp_wrclk_sels, ccm_base + 0xb980); + hws[IMX8MP_CLK_IPP_DO_CLKO1] = imx8m_clk_hw_composite("ipp_do_clko1", imx8mp_ipp_do_clko1_sels, ccm_base + 0xba00); + hws[IMX8MP_CLK_IPP_DO_CLKO2] = imx8m_clk_hw_composite("ipp_do_clko2", imx8mp_ipp_do_clko2_sels, ccm_base + 0xba80); + hws[IMX8MP_CLK_HDMI_FDCC_TST] = imx8m_clk_hw_composite("hdmi_fdcc_tst", imx8mp_hdmi_fdcc_tst_sels, ccm_base + 0xbb00); + hws[IMX8MP_CLK_HDMI_24M] = imx8m_clk_hw_composite("hdmi_24m", imx8mp_hdmi_24m_sels, ccm_base + 0xbb80); + hws[IMX8MP_CLK_HDMI_REF_266M] = imx8m_clk_hw_composite("hdmi_ref_266m", imx8mp_hdmi_ref_266m_sels, ccm_base + 0xbc00); + hws[IMX8MP_CLK_USDHC3] = imx8m_clk_hw_composite("usdhc3", imx8mp_usdhc3_sels, ccm_base + 0xbc80); + hws[IMX8MP_CLK_MEDIA_CAM1_PIX] = imx8m_clk_hw_composite("media_cam1_pix", imx8mp_media_cam1_pix_sels, ccm_base + 0xbd00); + hws[IMX8MP_CLK_MEDIA_MIPI_PHY1_REF] = imx8m_clk_hw_composite("media_mipi_phy1_ref", imx8mp_media_mipi_phy1_ref_sels, ccm_base + 0xbd80); + hws[IMX8MP_CLK_MEDIA_DISP1_PIX] = imx8m_clk_hw_composite("media_disp1_pix", imx8mp_media_disp_pix_sels, ccm_base + 0xbe00); + hws[IMX8MP_CLK_MEDIA_CAM2_PIX] = imx8m_clk_hw_composite("media_cam2_pix", imx8mp_media_cam2_pix_sels, ccm_base + 0xbe80); + hws[IMX8MP_CLK_MEDIA_LDB] = imx8m_clk_hw_composite("media_ldb", imx8mp_media_ldb_sels, ccm_base + 0xbf00); + hws[IMX8MP_CLK_MEMREPAIR] = imx8m_clk_hw_composite_critical("mem_repair", imx8mp_memrepair_sels, ccm_base + 0xbf80); + hws[IMX8MP_CLK_MEDIA_MIPI_TEST_BYTE] = imx8m_clk_hw_composite("media_mipi_test_byte", imx8mp_media_mipi_test_byte_sels, ccm_base + 0xc100); + hws[IMX8MP_CLK_ECSPI3] = imx8m_clk_hw_composite("ecspi3", imx8mp_ecspi3_sels, ccm_base + 0xc180); + hws[IMX8MP_CLK_PDM] = imx8m_clk_hw_composite("pdm", imx8mp_pdm_sels, ccm_base + 0xc200); + hws[IMX8MP_CLK_VPU_VC8000E] = imx8m_clk_hw_composite("vpu_vc8000e", imx8mp_vpu_vc8000e_sels, ccm_base + 0xc280); + hws[IMX8MP_CLK_SAI7] = imx8m_clk_hw_composite("sai7", imx8mp_sai7_sels, ccm_base + 0xc300); + + hws[IMX8MP_CLK_DRAM_ALT_ROOT] = imx_clk_hw_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + hws[IMX8MP_CLK_DRAM_CORE] = imx_clk_hw_mux2_flags("dram_core_clk", ccm_base + 0x9800, 24, 1, imx8mp_dram_core_sels, ARRAY_SIZE(imx8mp_dram_core_sels), CLK_IS_CRITICAL); + + hws[IMX8MP_CLK_DRAM1_ROOT] = imx_clk_hw_gate4_flags("dram1_root_clk", "dram_core_clk", ccm_base + 0x4050, 0, CLK_IS_CRITICAL); + hws[IMX8MP_CLK_ECSPI1_ROOT] = imx_clk_hw_gate4("ecspi1_root_clk", "ecspi1", ccm_base + 0x4070, 0); + hws[IMX8MP_CLK_ECSPI2_ROOT] = imx_clk_hw_gate4("ecspi2_root_clk", "ecspi2", ccm_base + 0x4080, 0); + hws[IMX8MP_CLK_ECSPI3_ROOT] = imx_clk_hw_gate4("ecspi3_root_clk", "ecspi3", ccm_base + 0x4090, 0); + hws[IMX8MP_CLK_ENET1_ROOT] = imx_clk_hw_gate4("enet1_root_clk", "enet_axi", ccm_base + 0x40a0, 0); + hws[IMX8MP_CLK_GPIO1_ROOT] = imx_clk_hw_gate4("gpio1_root_clk", "ipg_root", ccm_base + 0x40b0, 0); + hws[IMX8MP_CLK_GPIO2_ROOT] = imx_clk_hw_gate4("gpio2_root_clk", "ipg_root", ccm_base + 0x40c0, 0); + hws[IMX8MP_CLK_GPIO3_ROOT] = imx_clk_hw_gate4("gpio3_root_clk", "ipg_root", ccm_base + 0x40d0, 0); + hws[IMX8MP_CLK_GPIO4_ROOT] = imx_clk_hw_gate4("gpio4_root_clk", "ipg_root", ccm_base + 0x40e0, 0); + hws[IMX8MP_CLK_GPIO5_ROOT] = imx_clk_hw_gate4("gpio5_root_clk", "ipg_root", ccm_base + 0x40f0, 0); + hws[IMX8MP_CLK_GPT1_ROOT] = imx_clk_hw_gate4("gpt1_root_clk", "gpt1", ccm_base + 0x4100, 0); + hws[IMX8MP_CLK_GPT2_ROOT] = imx_clk_hw_gate4("gpt2_root_clk", "gpt2", ccm_base + 0x4110, 0); + hws[IMX8MP_CLK_GPT3_ROOT] = imx_clk_hw_gate4("gpt3_root_clk", "gpt3", ccm_base + 0x4120, 0); + hws[IMX8MP_CLK_GPT4_ROOT] = imx_clk_hw_gate4("gpt4_root_clk", "gpt4", ccm_base + 0x4130, 0); + hws[IMX8MP_CLK_GPT5_ROOT] = imx_clk_hw_gate4("gpt5_root_clk", "gpt5", ccm_base + 0x4140, 0); + hws[IMX8MP_CLK_GPT6_ROOT] = imx_clk_hw_gate4("gpt6_root_clk", "gpt6", ccm_base + 0x4150, 0); + hws[IMX8MP_CLK_I2C1_ROOT] = imx_clk_hw_gate4("i2c1_root_clk", "i2c1", ccm_base + 0x4170, 0); + hws[IMX8MP_CLK_I2C2_ROOT] = imx_clk_hw_gate4("i2c2_root_clk", "i2c2", ccm_base + 0x4180, 0); + hws[IMX8MP_CLK_I2C3_ROOT] = imx_clk_hw_gate4("i2c3_root_clk", "i2c3", ccm_base + 0x4190, 0); + hws[IMX8MP_CLK_I2C4_ROOT] = imx_clk_hw_gate4("i2c4_root_clk", "i2c4", ccm_base + 0x41a0, 0); + hws[IMX8MP_CLK_MU_ROOT] = imx_clk_hw_gate4("mu_root_clk", "ipg_root", ccm_base + 0x4210, 0); + hws[IMX8MP_CLK_OCOTP_ROOT] = imx_clk_hw_gate4("ocotp_root_clk", "ipg_root", ccm_base + 0x4220, 0); + hws[IMX8MP_CLK_PCIE_ROOT] = imx_clk_hw_gate4("pcie_root_clk", "pcie_aux", ccm_base + 0x4250, 0); + hws[IMX8MP_CLK_PWM1_ROOT] = imx_clk_hw_gate4("pwm1_root_clk", "pwm1", ccm_base + 0x4280, 0); + hws[IMX8MP_CLK_PWM2_ROOT] = imx_clk_hw_gate4("pwm2_root_clk", "pwm2", ccm_base + 0x4290, 0); + hws[IMX8MP_CLK_PWM3_ROOT] = imx_clk_hw_gate4("pwm3_root_clk", "pwm3", ccm_base + 0x42a0, 0); + hws[IMX8MP_CLK_PWM4_ROOT] = imx_clk_hw_gate4("pwm4_root_clk", "pwm4", ccm_base + 0x42b0, 0); + hws[IMX8MP_CLK_QOS_ROOT] = imx_clk_hw_gate4("qos_root_clk", "ipg_root", ccm_base + 0x42c0, 0); + hws[IMX8MP_CLK_QOS_ENET_ROOT] = imx_clk_hw_gate4("qos_enet_root_clk", "ipg_root", ccm_base + 0x42e0, 0); + hws[IMX8MP_CLK_QSPI_ROOT] = imx_clk_hw_gate4("qspi_root_clk", "qspi", ccm_base + 0x42f0, 0); + hws[IMX8MP_CLK_NAND_ROOT] = imx_clk_hw_gate2_shared2("nand_root_clk", "nand", ccm_base + 0x4300, 0, &share_count_nand); + hws[IMX8MP_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", ccm_base + 0x4300, 0, &share_count_nand); + hws[IMX8MP_CLK_I2C5_ROOT] = imx_clk_hw_gate2("i2c5_root_clk", "i2c5", ccm_base + 0x4330, 0); + hws[IMX8MP_CLK_I2C6_ROOT] = imx_clk_hw_gate2("i2c6_root_clk", "i2c6", ccm_base + 0x4340, 0); + hws[IMX8MP_CLK_CAN1_ROOT] = imx_clk_hw_gate2("can1_root_clk", "can1", ccm_base + 0x4350, 0); + hws[IMX8MP_CLK_CAN2_ROOT] = imx_clk_hw_gate2("can2_root_clk", "can2", ccm_base + 0x4360, 0); + hws[IMX8MP_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_root_clk", "ipg_root", ccm_base + 0x43a0, 0); + hws[IMX8MP_CLK_SIM_ENET_ROOT] = imx_clk_hw_gate4("sim_enet_root_clk", "enet_axi", ccm_base + 0x4400, 0); + hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0); + hws[IMX8MP_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_core", ccm_base + 0x4450, 0); + hws[IMX8MP_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core", ccm_base + 0x4460, 0); + hws[IMX8MP_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", ccm_base + 0x4490, 0); + hws[IMX8MP_CLK_UART2_ROOT] = imx_clk_hw_gate4("uart2_root_clk", "uart2", ccm_base + 0x44a0, 0); + hws[IMX8MP_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", ccm_base + 0x44b0, 0); + hws[IMX8MP_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", ccm_base + 0x44c0, 0); + hws[IMX8MP_CLK_USB_ROOT] = imx_clk_hw_gate2_shared2("usb_root_clk", "hsio_axi", ccm_base + 0x44d0, 0, &share_count_usb); + hws[IMX8MP_CLK_USB_SUSP] = imx_clk_hw_gate2_shared2("usb_suspend_clk", "osc_32k", ccm_base + 0x44d0, 0, &share_count_usb); + hws[IMX8MP_CLK_USB_PHY_ROOT] = imx_clk_hw_gate4("usb_phy_root_clk", "usb_phy_ref", ccm_base + 0x44f0, 0); + hws[IMX8MP_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", ccm_base + 0x4510, 0); + hws[IMX8MP_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", ccm_base + 0x4520, 0); + hws[IMX8MP_CLK_WDOG1_ROOT] = imx_clk_hw_gate4("wdog1_root_clk", "wdog", ccm_base + 0x4530, 0); + hws[IMX8MP_CLK_WDOG2_ROOT] = imx_clk_hw_gate4("wdog2_root_clk", "wdog", ccm_base + 0x4540, 0); + hws[IMX8MP_CLK_WDOG3_ROOT] = imx_clk_hw_gate4("wdog3_root_clk", "wdog", ccm_base + 0x4550, 0); + hws[IMX8MP_CLK_VPU_G1_ROOT] = imx_clk_hw_gate4("vpu_g1_root_clk", "vpu_g1", ccm_base + 0x4560, 0); + hws[IMX8MP_CLK_GPU_ROOT] = imx_clk_hw_gate4("gpu_root_clk", "gpu_axi", ccm_base + 0x4570, 0); + hws[IMX8MP_CLK_VPU_VC8KE_ROOT] = imx_clk_hw_gate4("vpu_vc8ke_root_clk", "vpu_vc8000e", ccm_base + 0x4590, 0); + hws[IMX8MP_CLK_VPU_G2_ROOT] = imx_clk_hw_gate4("vpu_g2_root_clk", "vpu_g2", ccm_base + 0x45a0, 0); + hws[IMX8MP_CLK_NPU_ROOT] = imx_clk_hw_gate4("npu_root_clk", "ml_core", ccm_base + 0x45b0, 0); + hws[IMX8MP_CLK_HSIO_ROOT] = imx_clk_hw_gate4("hsio_root_clk", "ipg_root", ccm_base + 0x45c0, 0); + hws[IMX8MP_CLK_MEDIA_APB_ROOT] = imx_clk_hw_gate2_shared2("media_apb_root_clk", "media_apb", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_AXI_ROOT] = imx_clk_hw_gate2_shared2("media_axi_root_clk", "media_axi", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_CAM1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_cam1_pix_root_clk", "media_cam1_pix", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_CAM2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_cam2_pix_root_clk", "media_cam2_pix", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp1_pix_root_clk", "media_disp1_pix", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp2_pix_root_clk", "media_disp2_pix", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_MIPI_PHY1_REF_ROOT] = imx_clk_hw_gate2_shared2("media_mipi_phy1_ref_root", "media_mipi_phy1_ref", ccm_base + 0x45d0, 0, &share_count_media); + hws[IMX8MP_CLK_MEDIA_ISP_ROOT] = imx_clk_hw_gate2_shared2("media_isp_root_clk", "media_isp", ccm_base + 0x45d0, 0, &share_count_media); + + hws[IMX8MP_CLK_USDHC3_ROOT] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3", ccm_base + 0x45e0, 0); + hws[IMX8MP_CLK_HDMI_ROOT] = imx_clk_hw_gate4("hdmi_root_clk", "hdmi_axi", ccm_base + 0x45f0, 0); + hws[IMX8MP_CLK_TSENSOR_ROOT] = imx_clk_hw_gate4("tsensor_root_clk", "ipg_root", ccm_base + 0x4620, 0); + hws[IMX8MP_CLK_VPU_ROOT] = imx_clk_hw_gate4("vpu_root_clk", "vpu_bus", ccm_base + 0x4630, 0); + hws[IMX8MP_CLK_AUDIO_ROOT] = imx_clk_hw_gate4("audio_root_clk", "audio_ahb", ccm_base + 0x4650, 0); + + hws[IMX8MP_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MP_CLK_A53_CORE]->clk, + hws[IMX8MP_CLK_A53_CORE]->clk, + hws[IMX8MP_ARM_PLL_OUT]->clk, + hws[IMX8MP_CLK_A53_DIV]->clk); + + imx_check_clk_hws(hws, IMX8MP_CLK_END); + + err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + if (err < 0) { + dev_err(dev, "failed to register hws for i.MX8MP\n"); + imx_unregister_hw_clocks(hws, IMX8MP_CLK_END); + return err; + } + + imx_register_uart_clocks(4); + + return 0; +} + +static const struct of_device_id imx8mp_clk_of_match[] = { + { .compatible = "fsl,imx8mp-ccm" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx8mp_clk_of_match); + +static struct platform_driver imx8mp_clk_driver = { + .probe = imx8mp_clocks_probe, + .driver = { + .name = "imx8mp-ccm", + /* + * Disable bind attributes: clocks are not removed and + * reloading the driver will crash or break devices. + */ + .suppress_bind_attrs = true, + .of_match_table = imx8mp_clk_of_match, + }, +}; +module_platform_driver(imx8mp_clk_driver); +module_param(mcore_booted, bool, S_IRUGO); +MODULE_PARM_DESC(mcore_booted, "See Cortex-M core is booted or not"); + +MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); +MODULE_DESCRIPTION("NXP i.MX8MP clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c new file mode 100644 index 000000000..0a75814b3 --- /dev/null +++ b/drivers/clk/imx/clk-imx8mq.c @@ -0,0 +1,642 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 NXP. + * Copyright (C) 2017 Pengutronix, Lucas Stach <kernel@pengutronix.de> + */ + +#include <dt-bindings/clock/imx8mq-clock.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include "clk.h" + +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_sai4; +static u32 share_count_sai5; +static u32 share_count_sai6; +static u32 share_count_dcss; +static u32 share_count_nand; + +static const char * const pll_ref_sels[] = { "osc_25m", "osc_27m", "hdmi_phy_27m", "dummy", }; +static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; + +static const char * const sys3_pll_out_sels[] = {"sys3_pll1_ref_sel", }; +static const char * const dram_pll_out_sels[] = {"dram_pll1_ref_sel", }; +static const char * const video2_pll_out_sels[] = {"video2_pll1_ref_sel", }; + +/* CCM ROOT */ +static const char * const imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", + "sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "sys3_pll_out", }; + +static const char * const imx8mq_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + +static const char * const imx8mq_arm_m4_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_250m", "sys1_pll_266m", + "sys1_pll_800m", "audio_pll1_out", "video_pll1_out", "sys3_pll_out", }; + +static const char * const imx8mq_vpu_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", + "sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "vpu_pll_out", }; + +static const char * const imx8mq_gpu_core_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll_out", + "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mq_gpu_shader_sels[] = {"osc_25m", "gpu_pll_out", "sys1_pll_800m", "sys3_pll_out", + "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mq_main_axi_sels[] = {"osc_25m", "sys2_pll_333m", "sys1_pll_800m", "sys2_pll_250m", + "sys2_pll_1000m", "audio_pll1_out", "video_pll1_out", "sys1_pll_100m",}; + +static const char * const imx8mq_enet_axi_sels[] = {"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 * const imx8mq_nand_usdhc_sels[] = {"osc_25m", "sys1_pll_266m", "sys1_pll_800m", "sys2_pll_200m", + "sys1_pll_133m", "sys3_pll_out", "sys2_pll_250m", "audio_pll1_out", }; + +static const char * const imx8mq_vpu_bus_sels[] = {"osc_25m", "sys1_pll_800m", "vpu_pll_out", "audio_pll2_out", "sys3_pll_out", "sys2_pll_1000m", "sys2_pll_200m", "sys1_pll_100m", }; + +static const char * const imx8mq_disp_axi_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll_out", "sys1_pll_400m", "audio_pll2_out", "clk_ext1", "clk_ext4", }; + +static const char * const imx8mq_disp_apb_sels[] = {"osc_25m", "sys2_pll_125m", "sys1_pll_800m", "sys3_pll_out", + "sys1_pll_40m", "audio_pll2_out", "clk_ext1", "clk_ext3", }; + +static const char * const imx8mq_disp_rtrm_sels[] = {"osc_25m", "sys1_pll_800m", "sys2_pll_200m", "sys1_pll_400m", + "audio_pll1_out", "video_pll1_out", "clk_ext2", "clk_ext3", }; + +static const char * const imx8mq_usb_bus_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_100m", + "sys2_pll_200m", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mq_gpu_axi_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll_out", "sys2_pll_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mq_gpu_ahb_sels[] = {"osc_25m", "sys1_pll_800m", "gpu_pll_out", "sys3_pll_out", "sys2_pll_1000m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mq_noc_sels[] = {"osc_25m", "sys1_pll_800m", "sys3_pll_out", "sys2_pll_1000m", "sys2_pll_500m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mq_noc_apb_sels[] = {"osc_25m", "sys1_pll_400m", "sys3_pll_out", "sys2_pll_333m", "sys2_pll_200m", + "sys1_pll_800m", "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mq_ahb_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_800m", "sys1_pll_400m", + "sys2_pll_125m", "sys3_pll_out", "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mq_audio_ahb_sels[] = {"osc_25m", "sys2_pll_500m", "sys1_pll_800m", "sys2_pll_1000m", + "sys2_pll_166m", "sys3_pll_out", "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mq_dsi_ahb_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "clk_ext3", "audio_pll2_out"}; + +static const char * const imx8mq_dram_alt_sels[] = {"osc_25m", "sys1_pll_800m", "sys1_pll_100m", "sys2_pll_500m", + "sys2_pll_250m", "sys1_pll_400m", "audio_pll1_out", "sys1_pll_266m", }; + +static const char * const imx8mq_dram_apb_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", + "sys1_pll_800m", "sys3_pll_out", "sys2_pll_250m", "audio_pll2_out", }; + +static const char * const imx8mq_vpu_g1_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll_out", "audio_pll1_out", }; + +static const char * const imx8mq_vpu_g2_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_100m", "sys2_pll_125m", "sys3_pll_out", "audio_pll1_out", }; + +static const char * const imx8mq_disp_dtrc_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll_out", "audio_pll2_out", }; + +static const char * const imx8mq_disp_dc8000_sels[] = {"osc_25m", "vpu_pll_out", "sys1_pll_800m", "sys2_pll_1000m", "sys1_pll_160m", "sys2_pll_100m", "sys3_pll_out", "audio_pll2_out", }; + +static const char * const imx8mq_pcie1_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m", + "sys1_pll_800m", "sys2_pll_500m", "sys2_pll_333m", "sys3_pll_out", }; + +static const char * const imx8mq_pcie1_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mq_pcie1_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_50m", "sys3_pll_out", + "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", }; + +static const char * const imx8mq_dc_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll_out", "clk_ext4", }; + +static const char * const imx8mq_lcdif_pixel_sels[] = {"osc_25m", "video_pll1_out", "audio_pll2_out", "audio_pll1_out", "sys1_pll_800m", "sys2_pll_1000m", "sys3_pll_out", "clk_ext4", }; + +static const char * const imx8mq_sai1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", }; + +static const char * const imx8mq_sai2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; + +static const char * const imx8mq_sai3_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; + +static const char * const imx8mq_sai4_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext1", "clk_ext2", }; + +static const char * const imx8mq_sai5_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; + +static const char * const imx8mq_sai6_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; + +static const char * const imx8mq_spdif1_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext2", "clk_ext3", }; + +static const char * const imx8mq_spdif2_sels[] = {"osc_25m", "audio_pll1_out", "audio_pll2_out", "video_pll1_out", "sys1_pll_133m", "osc_27m", "clk_ext3", "clk_ext4", }; + +static const char * const imx8mq_enet_ref_sels[] = {"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 * const imx8mq_enet_timer_sels[] = {"osc_25m", "sys2_pll_100m", "audio_pll1_out", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4", "video_pll1_out", }; + +static const char * const imx8mq_enet_phy_sels[] = {"osc_25m", "sys2_pll_50m", "sys2_pll_125m", "sys2_pll_500m", + "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mq_nand_sels[] = {"osc_25m", "sys2_pll_500m", "audio_pll1_out", "sys1_pll_400m", + "audio_pll2_out", "sys3_pll_out", "sys2_pll_250m", "video_pll1_out", }; + +static const char * const imx8mq_qspi_sels[] = {"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 * const imx8mq_usdhc1_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", + "sys3_pll_out", "sys1_pll_266m", "audio_pll2_out", "sys1_pll_100m", }; + +static const char * const imx8mq_usdhc2_sels[] = {"osc_25m", "sys1_pll_400m", "sys1_pll_800m", "sys2_pll_500m", + "sys3_pll_out", "sys1_pll_266m", "audio_pll2_out", "sys1_pll_100m", }; + +static const char * const imx8mq_i2c1_sels[] = {"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 * const imx8mq_i2c2_sels[] = {"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 * const imx8mq_i2c3_sels[] = {"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 * const imx8mq_i2c4_sels[] = {"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 * const imx8mq_uart1_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", + "sys3_pll_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mq_uart2_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", + "sys3_pll_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mq_uart3_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", + "sys3_pll_out", "clk_ext2", "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mq_uart4_sels[] = {"osc_25m", "sys1_pll_80m", "sys2_pll_200m", "sys2_pll_100m", + "sys3_pll_out", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mq_usb_core_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", + "sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mq_usb_phy_sels[] = {"osc_25m", "sys1_pll_100m", "sys1_pll_40m", "sys2_pll_100m", + "sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mq_gic_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys2_pll_100m", + "sys2_pll_200m", "clk_ext2", "clk_ext3", "audio_pll2_out" }; + +static const char * const imx8mq_ecspi1_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", + "sys1_pll_800m", "sys3_pll_out", "sys2_pll_250m", "audio_pll2_out", }; + +static const char * const imx8mq_ecspi2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", + "sys1_pll_800m", "sys3_pll_out", "sys2_pll_250m", "audio_pll2_out", }; + +static const char * const imx8mq_pwm1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", + "sys3_pll_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", }; + +static const char * const imx8mq_pwm2_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", + "sys3_pll_out", "clk_ext1", "sys1_pll_80m", "video_pll1_out", }; + +static const char * const imx8mq_pwm3_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", + "sys3_pll_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", }; + +static const char * const imx8mq_pwm4_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_160m", "sys1_pll_40m", + "sys3_pll_out", "clk_ext2", "sys1_pll_80m", "video_pll1_out", }; + +static const char * const imx8mq_gpt1_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_400m", "sys1_pll_40m", + "sys1_pll_80m", "audio_pll1_out", "clk_ext1", }; + +static const char * const imx8mq_wdog_sels[] = {"osc_25m", "sys1_pll_133m", "sys1_pll_160m", "vpu_pll_out", + "sys2_pll_125m", "sys3_pll_out", "sys1_pll_80m", "sys2_pll_166m", }; + +static const char * const imx8mq_wrclk_sels[] = {"osc_25m", "sys1_pll_40m", "vpu_pll_out", "sys3_pll_out", "sys2_pll_200m", + "sys1_pll_266m", "sys2_pll_500m", "sys1_pll_100m", }; + +static const char * const imx8mq_dsi_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mq_dsi_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", + "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mq_dsi_dbi_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_100m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", + "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m", + "sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m", + "sys2_pll_1000m", "sys3_pll_out", "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mq_pcie2_ctrl_sels[] = {"osc_25m", "sys2_pll_250m", "sys2_pll_200m", "sys1_pll_266m", + "sys1_pll_800m", "sys2_pll_500m", "sys2_pll_333m", "sys3_pll_out", }; + +static const char * const imx8mq_pcie2_phy_sels[] = {"osc_25m", "sys2_pll_100m", "sys2_pll_500m", "clk_ext1", + "clk_ext2", "clk_ext3", "clk_ext4", "sys1_pll_400m", }; + +static const char * const imx8mq_pcie2_aux_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_50m", "sys3_pll_out", + "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_160m", "sys1_pll_200m", }; + +static const char * const imx8mq_ecspi3_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_40m", "sys1_pll_160m", + "sys1_pll_800m", "sys3_pll_out", "sys2_pll_250m", "audio_pll2_out", }; +static const char * const imx8mq_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; + +static const char * const imx8mq_clko1_sels[] = {"osc_25m", "sys1_pll_800m", "osc_27m", "sys1_pll_200m", + "audio_pll2_out", "sys2_pll_500m", "vpu_pll_out", "sys1_pll_80m", }; +static const char * const imx8mq_clko2_sels[] = {"osc_25m", "sys2_pll_200m", "sys1_pll_400m", "sys2_pll_166m", + "sys3_pll_out", "audio_pll1_out", "video_pll1_out", "ckil", }; + +static const char * const pllout_monitor_sels[] = {"osc_25m", "osc_27m", "dummy", "dummy", "ckil", + "audio_pll1_out_monitor", "audio_pll2_out_monitor", + "video_pll1_out_monitor", "gpu_pll_out_monitor", + "vpu_pll_out_monitor", "arm_pll_out_monitor", + "sys_pll1_out_monitor", "sys_pll2_out_monitor", + "sys_pll3_out_monitor", "dram_pll_out_monitor", + "video_pll2_out_monitor", }; + +static struct clk_hw_onecell_data *clk_hw_data; +static struct clk_hw **hws; + +static int imx8mq_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + int err; + + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, IMX8MQ_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return -ENOMEM; + + clk_hw_data->num = IMX8MQ_CLK_END; + hws = clk_hw_data->hws; + + hws[IMX8MQ_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + hws[IMX8MQ_CLK_32K] = imx_obtain_fixed_clk_hw(np, "ckil"); + hws[IMX8MQ_CLK_25M] = imx_obtain_fixed_clk_hw(np, "osc_25m"); + hws[IMX8MQ_CLK_27M] = imx_obtain_fixed_clk_hw(np, "osc_27m"); + hws[IMX8MQ_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1"); + hws[IMX8MQ_CLK_EXT2] = imx_obtain_fixed_clk_hw(np, "clk_ext2"); + hws[IMX8MQ_CLK_EXT3] = imx_obtain_fixed_clk_hw(np, "clk_ext3"); + hws[IMX8MQ_CLK_EXT4] = imx_obtain_fixed_clk_hw(np, "clk_ext4"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-anatop"); + base = devm_of_iomap(dev, np, 0, NULL); + of_node_put(np); + if (WARN_ON(IS_ERR(base))) { + err = PTR_ERR(base); + goto unregister_hws; + } + + hws[IMX8MQ_ARM_PLL_REF_SEL] = imx_clk_hw_mux("arm_pll_ref_sel", base + 0x28, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_GPU_PLL_REF_SEL] = imx_clk_hw_mux("gpu_pll_ref_sel", base + 0x18, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_VPU_PLL_REF_SEL] = imx_clk_hw_mux("vpu_pll_ref_sel", base + 0x20, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_AUDIO_PLL1_REF_SEL] = imx_clk_hw_mux("audio_pll1_ref_sel", base + 0x0, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_AUDIO_PLL2_REF_SEL] = imx_clk_hw_mux("audio_pll2_ref_sel", base + 0x8, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_VIDEO_PLL1_REF_SEL] = imx_clk_hw_mux("video_pll1_ref_sel", base + 0x10, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_SYS3_PLL1_REF_SEL] = imx_clk_hw_mux("sys3_pll1_ref_sel", base + 0x48, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_DRAM_PLL1_REF_SEL] = imx_clk_hw_mux("dram_pll1_ref_sel", base + 0x60, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMX8MQ_VIDEO2_PLL1_REF_SEL] = imx_clk_hw_mux("video2_pll1_ref_sel", base + 0x54, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + + hws[IMX8MQ_ARM_PLL_REF_DIV] = imx_clk_hw_divider("arm_pll_ref_div", "arm_pll_ref_sel", base + 0x28, 5, 6); + hws[IMX8MQ_GPU_PLL_REF_DIV] = imx_clk_hw_divider("gpu_pll_ref_div", "gpu_pll_ref_sel", base + 0x18, 5, 6); + hws[IMX8MQ_VPU_PLL_REF_DIV] = imx_clk_hw_divider("vpu_pll_ref_div", "vpu_pll_ref_sel", base + 0x20, 5, 6); + hws[IMX8MQ_AUDIO_PLL1_REF_DIV] = imx_clk_hw_divider("audio_pll1_ref_div", "audio_pll1_ref_sel", base + 0x0, 5, 6); + hws[IMX8MQ_AUDIO_PLL2_REF_DIV] = imx_clk_hw_divider("audio_pll2_ref_div", "audio_pll2_ref_sel", base + 0x8, 5, 6); + hws[IMX8MQ_VIDEO_PLL1_REF_DIV] = imx_clk_hw_divider("video_pll1_ref_div", "video_pll1_ref_sel", base + 0x10, 5, 6); + + hws[IMX8MQ_ARM_PLL] = imx_clk_hw_frac_pll("arm_pll", "arm_pll_ref_div", base + 0x28); + hws[IMX8MQ_GPU_PLL] = imx_clk_hw_frac_pll("gpu_pll", "gpu_pll_ref_div", base + 0x18); + hws[IMX8MQ_VPU_PLL] = imx_clk_hw_frac_pll("vpu_pll", "vpu_pll_ref_div", base + 0x20); + hws[IMX8MQ_AUDIO_PLL1] = imx_clk_hw_frac_pll("audio_pll1", "audio_pll1_ref_div", base + 0x0); + hws[IMX8MQ_AUDIO_PLL2] = imx_clk_hw_frac_pll("audio_pll2", "audio_pll2_ref_div", base + 0x8); + hws[IMX8MQ_VIDEO_PLL1] = imx_clk_hw_frac_pll("video_pll1", "video_pll1_ref_div", base + 0x10); + + /* PLL bypass out */ + hws[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_hw_mux_flags("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMX8MQ_GPU_PLL_BYPASS] = imx_clk_hw_mux("gpu_pll_bypass", base + 0x18, 14, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels)); + hws[IMX8MQ_VPU_PLL_BYPASS] = imx_clk_hw_mux("vpu_pll_bypass", base + 0x20, 14, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels)); + hws[IMX8MQ_AUDIO_PLL1_BYPASS] = imx_clk_hw_mux("audio_pll1_bypass", base + 0x0, 14, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels)); + hws[IMX8MQ_AUDIO_PLL2_BYPASS] = imx_clk_hw_mux("audio_pll2_bypass", base + 0x8, 14, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels)); + hws[IMX8MQ_VIDEO_PLL1_BYPASS] = imx_clk_hw_mux("video_pll1_bypass", base + 0x10, 14, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels)); + + /* PLL OUT GATE */ + hws[IMX8MQ_ARM_PLL_OUT] = imx_clk_hw_gate("arm_pll_out", "arm_pll_bypass", base + 0x28, 21); + hws[IMX8MQ_GPU_PLL_OUT] = imx_clk_hw_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x18, 21); + hws[IMX8MQ_VPU_PLL_OUT] = imx_clk_hw_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x20, 21); + hws[IMX8MQ_AUDIO_PLL1_OUT] = imx_clk_hw_gate("audio_pll1_out", "audio_pll1_bypass", base + 0x0, 21); + hws[IMX8MQ_AUDIO_PLL2_OUT] = imx_clk_hw_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x8, 21); + hws[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_hw_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21); + + hws[IMX8MQ_SYS1_PLL_OUT] = imx_clk_hw_fixed("sys1_pll_out", 800000000); + hws[IMX8MQ_SYS2_PLL_OUT] = imx_clk_hw_fixed("sys2_pll_out", 1000000000); + hws[IMX8MQ_SYS3_PLL_OUT] = imx_clk_hw_sscg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 0, base + 0x48, CLK_IS_CRITICAL); + hws[IMX8MQ_DRAM_PLL_OUT] = imx_clk_hw_sscg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); + hws[IMX8MQ_VIDEO2_PLL_OUT] = imx_clk_hw_sscg_pll("video2_pll_out", video2_pll_out_sels, ARRAY_SIZE(video2_pll_out_sels), 0, 0, 0, base + 0x54, 0); + + /* SYS PLL1 fixed output */ + hws[IMX8MQ_SYS1_PLL_40M] = imx_clk_hw_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20); + hws[IMX8MQ_SYS1_PLL_80M] = imx_clk_hw_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10); + hws[IMX8MQ_SYS1_PLL_100M] = imx_clk_hw_fixed_factor("sys1_pll_100m", "sys1_pll_out", 1, 8); + hws[IMX8MQ_SYS1_PLL_133M] = imx_clk_hw_fixed_factor("sys1_pll_133m", "sys1_pll_out", 1, 6); + hws[IMX8MQ_SYS1_PLL_160M] = imx_clk_hw_fixed_factor("sys1_pll_160m", "sys1_pll_out", 1, 5); + hws[IMX8MQ_SYS1_PLL_200M] = imx_clk_hw_fixed_factor("sys1_pll_200m", "sys1_pll_out", 1, 4); + hws[IMX8MQ_SYS1_PLL_266M] = imx_clk_hw_fixed_factor("sys1_pll_266m", "sys1_pll_out", 1, 3); + hws[IMX8MQ_SYS1_PLL_400M] = imx_clk_hw_fixed_factor("sys1_pll_400m", "sys1_pll_out", 1, 2); + hws[IMX8MQ_SYS1_PLL_800M] = imx_clk_hw_fixed_factor("sys1_pll_800m", "sys1_pll_out", 1, 1); + + /* SYS PLL2 fixed output */ + hws[IMX8MQ_SYS2_PLL_50M] = imx_clk_hw_fixed_factor("sys2_pll_50m", "sys2_pll_out", 1, 20); + hws[IMX8MQ_SYS2_PLL_100M] = imx_clk_hw_fixed_factor("sys2_pll_100m", "sys2_pll_out", 1, 10); + hws[IMX8MQ_SYS2_PLL_125M] = imx_clk_hw_fixed_factor("sys2_pll_125m", "sys2_pll_out", 1, 8); + hws[IMX8MQ_SYS2_PLL_166M] = imx_clk_hw_fixed_factor("sys2_pll_166m", "sys2_pll_out", 1, 6); + hws[IMX8MQ_SYS2_PLL_200M] = imx_clk_hw_fixed_factor("sys2_pll_200m", "sys2_pll_out", 1, 5); + hws[IMX8MQ_SYS2_PLL_250M] = imx_clk_hw_fixed_factor("sys2_pll_250m", "sys2_pll_out", 1, 4); + hws[IMX8MQ_SYS2_PLL_333M] = imx_clk_hw_fixed_factor("sys2_pll_333m", "sys2_pll_out", 1, 3); + hws[IMX8MQ_SYS2_PLL_500M] = imx_clk_hw_fixed_factor("sys2_pll_500m", "sys2_pll_out", 1, 2); + hws[IMX8MQ_SYS2_PLL_1000M] = imx_clk_hw_fixed_factor("sys2_pll_1000m", "sys2_pll_out", 1, 1); + + hws[IMX8MQ_CLK_MON_AUDIO_PLL1_DIV] = imx_clk_hw_divider("audio_pll1_out_monitor", "audio_pll1_bypass", base + 0x78, 0, 3); + hws[IMX8MQ_CLK_MON_AUDIO_PLL2_DIV] = imx_clk_hw_divider("audio_pll2_out_monitor", "audio_pll2_bypass", base + 0x78, 4, 3); + hws[IMX8MQ_CLK_MON_VIDEO_PLL1_DIV] = imx_clk_hw_divider("video_pll1_out_monitor", "video_pll1_bypass", base + 0x78, 8, 3); + hws[IMX8MQ_CLK_MON_GPU_PLL_DIV] = imx_clk_hw_divider("gpu_pll_out_monitor", "gpu_pll_bypass", base + 0x78, 12, 3); + hws[IMX8MQ_CLK_MON_VPU_PLL_DIV] = imx_clk_hw_divider("vpu_pll_out_monitor", "vpu_pll_bypass", base + 0x78, 16, 3); + hws[IMX8MQ_CLK_MON_ARM_PLL_DIV] = imx_clk_hw_divider("arm_pll_out_monitor", "arm_pll_bypass", base + 0x78, 20, 3); + hws[IMX8MQ_CLK_MON_SYS_PLL1_DIV] = imx_clk_hw_divider("sys_pll1_out_monitor", "sys1_pll_out", base + 0x7c, 0, 3); + hws[IMX8MQ_CLK_MON_SYS_PLL2_DIV] = imx_clk_hw_divider("sys_pll2_out_monitor", "sys2_pll_out", base + 0x7c, 4, 3); + hws[IMX8MQ_CLK_MON_SYS_PLL3_DIV] = imx_clk_hw_divider("sys_pll3_out_monitor", "sys3_pll_out", base + 0x7c, 8, 3); + hws[IMX8MQ_CLK_MON_DRAM_PLL_DIV] = imx_clk_hw_divider("dram_pll_out_monitor", "dram_pll_out", base + 0x7c, 12, 3); + hws[IMX8MQ_CLK_MON_VIDEO_PLL2_DIV] = imx_clk_hw_divider("video_pll2_out_monitor", "video2_pll_out", base + 0x7c, 16, 3); + hws[IMX8MQ_CLK_MON_SEL] = imx_clk_hw_mux("pllout_monitor_sel", base + 0x74, 0, 4, pllout_monitor_sels, ARRAY_SIZE(pllout_monitor_sels)); + hws[IMX8MQ_CLK_MON_CLK2_OUT] = imx_clk_hw_gate("pllout_monitor_clk2", "pllout_monitor_sel", base + 0x74, 4); + + np = dev->of_node; + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) { + err = PTR_ERR(base); + goto unregister_hws; + } + + /* CORE */ + hws[IMX8MQ_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mq_a53_sels, base + 0x8000); + hws[IMX8MQ_CLK_A53_CG] = hws[IMX8MQ_CLK_A53_DIV]; + hws[IMX8MQ_CLK_A53_SRC] = hws[IMX8MQ_CLK_A53_DIV]; + + hws[IMX8MQ_CLK_M4_CORE] = imx8m_clk_hw_composite_core("arm_m4_core", imx8mq_arm_m4_sels, base + 0x8080); + hws[IMX8MQ_CLK_VPU_CORE] = imx8m_clk_hw_composite_core("vpu_core", imx8mq_vpu_sels, base + 0x8100); + hws[IMX8MQ_CLK_GPU_CORE] = imx8m_clk_hw_composite_core("gpu_core", imx8mq_gpu_core_sels, base + 0x8180); + hws[IMX8MQ_CLK_GPU_SHADER] = imx8m_clk_hw_composite("gpu_shader", imx8mq_gpu_shader_sels, base + 0x8200); + /* For backwards compatibility */ + hws[IMX8MQ_CLK_M4_SRC] = hws[IMX8MQ_CLK_M4_CORE]; + hws[IMX8MQ_CLK_M4_CG] = hws[IMX8MQ_CLK_M4_CORE]; + hws[IMX8MQ_CLK_M4_DIV] = hws[IMX8MQ_CLK_M4_CORE]; + hws[IMX8MQ_CLK_VPU_SRC] = hws[IMX8MQ_CLK_VPU_CORE]; + hws[IMX8MQ_CLK_VPU_CG] = hws[IMX8MQ_CLK_VPU_CORE]; + hws[IMX8MQ_CLK_VPU_DIV] = hws[IMX8MQ_CLK_VPU_CORE]; + hws[IMX8MQ_CLK_GPU_CORE_SRC] = hws[IMX8MQ_CLK_GPU_CORE]; + hws[IMX8MQ_CLK_GPU_CORE_CG] = hws[IMX8MQ_CLK_GPU_CORE]; + hws[IMX8MQ_CLK_GPU_CORE_DIV] = hws[IMX8MQ_CLK_GPU_CORE]; + hws[IMX8MQ_CLK_GPU_SHADER_SRC] = hws[IMX8MQ_CLK_GPU_SHADER]; + hws[IMX8MQ_CLK_GPU_SHADER_CG] = hws[IMX8MQ_CLK_GPU_SHADER]; + hws[IMX8MQ_CLK_GPU_SHADER_DIV] = hws[IMX8MQ_CLK_GPU_SHADER]; + + /* CORE SEL */ + hws[IMX8MQ_CLK_A53_CORE] = imx_clk_hw_mux2("arm_a53_core", base + 0x9880, 24, 1, imx8mq_a53_core_sels, ARRAY_SIZE(imx8mq_a53_core_sels)); + + /* BUS */ + hws[IMX8MQ_CLK_MAIN_AXI] = imx8m_clk_hw_composite_bus_critical("main_axi", imx8mq_main_axi_sels, base + 0x8800); + hws[IMX8MQ_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mq_enet_axi_sels, base + 0x8880); + hws[IMX8MQ_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus("nand_usdhc_bus", imx8mq_nand_usdhc_sels, base + 0x8900); + hws[IMX8MQ_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mq_vpu_bus_sels, base + 0x8980); + hws[IMX8MQ_CLK_DISP_AXI] = imx8m_clk_hw_composite_bus("disp_axi", imx8mq_disp_axi_sels, base + 0x8a00); + hws[IMX8MQ_CLK_DISP_APB] = imx8m_clk_hw_composite_bus("disp_apb", imx8mq_disp_apb_sels, base + 0x8a80); + hws[IMX8MQ_CLK_DISP_RTRM] = imx8m_clk_hw_composite_bus("disp_rtrm", imx8mq_disp_rtrm_sels, base + 0x8b00); + hws[IMX8MQ_CLK_USB_BUS] = imx8m_clk_hw_composite_bus("usb_bus", imx8mq_usb_bus_sels, base + 0x8b80); + hws[IMX8MQ_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mq_gpu_axi_sels, base + 0x8c00); + hws[IMX8MQ_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mq_gpu_ahb_sels, base + 0x8c80); + hws[IMX8MQ_CLK_NOC] = imx8m_clk_hw_composite_bus_critical("noc", imx8mq_noc_sels, base + 0x8d00); + hws[IMX8MQ_CLK_NOC_APB] = imx8m_clk_hw_composite_bus_critical("noc_apb", imx8mq_noc_apb_sels, base + 0x8d80); + + /* AHB */ + /* AHB clock is used by the AHB bus therefore marked as critical */ + hws[IMX8MQ_CLK_AHB] = imx8m_clk_hw_composite_bus_critical("ahb", imx8mq_ahb_sels, base + 0x9000); + hws[IMX8MQ_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mq_audio_ahb_sels, base + 0x9100); + + /* IPG */ + hws[IMX8MQ_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb", base + 0x9080, 0, 1); + hws[IMX8MQ_CLK_IPG_AUDIO_ROOT] = imx_clk_hw_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1); + + /* + * DRAM clocks are manipulated from TF-A outside clock framework. + * The fw_managed helper sets GET_RATE_NOCACHE and clears SET_PARENT_GATE + * as div value should always be read from hardware + */ + hws[IMX8MQ_CLK_DRAM_CORE] = imx_clk_hw_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mq_dram_core_sels, ARRAY_SIZE(imx8mq_dram_core_sels), CLK_IS_CRITICAL); + hws[IMX8MQ_CLK_DRAM_ALT] = imx8m_clk_hw_fw_managed_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000); + hws[IMX8MQ_CLK_DRAM_APB] = imx8m_clk_hw_fw_managed_composite_critical("dram_apb", imx8mq_dram_apb_sels, base + 0xa080); + + /* IP */ + hws[IMX8MQ_CLK_VPU_G1] = imx8m_clk_hw_composite("vpu_g1", imx8mq_vpu_g1_sels, base + 0xa100); + hws[IMX8MQ_CLK_VPU_G2] = imx8m_clk_hw_composite("vpu_g2", imx8mq_vpu_g2_sels, base + 0xa180); + hws[IMX8MQ_CLK_DISP_DTRC] = imx8m_clk_hw_composite("disp_dtrc", imx8mq_disp_dtrc_sels, base + 0xa200); + hws[IMX8MQ_CLK_DISP_DC8000] = imx8m_clk_hw_composite("disp_dc8000", imx8mq_disp_dc8000_sels, base + 0xa280); + hws[IMX8MQ_CLK_PCIE1_CTRL] = imx8m_clk_hw_composite("pcie1_ctrl", imx8mq_pcie1_ctrl_sels, base + 0xa300); + hws[IMX8MQ_CLK_PCIE1_PHY] = imx8m_clk_hw_composite("pcie1_phy", imx8mq_pcie1_phy_sels, base + 0xa380); + hws[IMX8MQ_CLK_PCIE1_AUX] = imx8m_clk_hw_composite("pcie1_aux", imx8mq_pcie1_aux_sels, base + 0xa400); + hws[IMX8MQ_CLK_DC_PIXEL] = imx8m_clk_hw_composite("dc_pixel", imx8mq_dc_pixel_sels, base + 0xa480); + hws[IMX8MQ_CLK_LCDIF_PIXEL] = imx8m_clk_hw_composite("lcdif_pixel", imx8mq_lcdif_pixel_sels, base + 0xa500); + hws[IMX8MQ_CLK_SAI1] = imx8m_clk_hw_composite("sai1", imx8mq_sai1_sels, base + 0xa580); + hws[IMX8MQ_CLK_SAI2] = imx8m_clk_hw_composite("sai2", imx8mq_sai2_sels, base + 0xa600); + hws[IMX8MQ_CLK_SAI3] = imx8m_clk_hw_composite("sai3", imx8mq_sai3_sels, base + 0xa680); + hws[IMX8MQ_CLK_SAI4] = imx8m_clk_hw_composite("sai4", imx8mq_sai4_sels, base + 0xa700); + hws[IMX8MQ_CLK_SAI5] = imx8m_clk_hw_composite("sai5", imx8mq_sai5_sels, base + 0xa780); + hws[IMX8MQ_CLK_SAI6] = imx8m_clk_hw_composite("sai6", imx8mq_sai6_sels, base + 0xa800); + hws[IMX8MQ_CLK_SPDIF1] = imx8m_clk_hw_composite("spdif1", imx8mq_spdif1_sels, base + 0xa880); + hws[IMX8MQ_CLK_SPDIF2] = imx8m_clk_hw_composite("spdif2", imx8mq_spdif2_sels, base + 0xa900); + hws[IMX8MQ_CLK_ENET_REF] = imx8m_clk_hw_composite("enet_ref", imx8mq_enet_ref_sels, base + 0xa980); + hws[IMX8MQ_CLK_ENET_TIMER] = imx8m_clk_hw_composite("enet_timer", imx8mq_enet_timer_sels, base + 0xaa00); + hws[IMX8MQ_CLK_ENET_PHY_REF] = imx8m_clk_hw_composite("enet_phy", imx8mq_enet_phy_sels, base + 0xaa80); + hws[IMX8MQ_CLK_NAND] = imx8m_clk_hw_composite("nand", imx8mq_nand_sels, base + 0xab00); + hws[IMX8MQ_CLK_QSPI] = imx8m_clk_hw_composite("qspi", imx8mq_qspi_sels, base + 0xab80); + hws[IMX8MQ_CLK_USDHC1] = imx8m_clk_hw_composite("usdhc1", imx8mq_usdhc1_sels, base + 0xac00); + hws[IMX8MQ_CLK_USDHC2] = imx8m_clk_hw_composite("usdhc2", imx8mq_usdhc2_sels, base + 0xac80); + hws[IMX8MQ_CLK_I2C1] = imx8m_clk_hw_composite("i2c1", imx8mq_i2c1_sels, base + 0xad00); + hws[IMX8MQ_CLK_I2C2] = imx8m_clk_hw_composite("i2c2", imx8mq_i2c2_sels, base + 0xad80); + hws[IMX8MQ_CLK_I2C3] = imx8m_clk_hw_composite("i2c3", imx8mq_i2c3_sels, base + 0xae00); + hws[IMX8MQ_CLK_I2C4] = imx8m_clk_hw_composite("i2c4", imx8mq_i2c4_sels, base + 0xae80); + hws[IMX8MQ_CLK_UART1] = imx8m_clk_hw_composite("uart1", imx8mq_uart1_sels, base + 0xaf00); + hws[IMX8MQ_CLK_UART2] = imx8m_clk_hw_composite("uart2", imx8mq_uart2_sels, base + 0xaf80); + hws[IMX8MQ_CLK_UART3] = imx8m_clk_hw_composite("uart3", imx8mq_uart3_sels, base + 0xb000); + hws[IMX8MQ_CLK_UART4] = imx8m_clk_hw_composite("uart4", imx8mq_uart4_sels, base + 0xb080); + hws[IMX8MQ_CLK_USB_CORE_REF] = imx8m_clk_hw_composite("usb_core_ref", imx8mq_usb_core_sels, base + 0xb100); + hws[IMX8MQ_CLK_USB_PHY_REF] = imx8m_clk_hw_composite("usb_phy_ref", imx8mq_usb_phy_sels, base + 0xb180); + hws[IMX8MQ_CLK_GIC] = imx8m_clk_hw_composite_critical("gic", imx8mq_gic_sels, base + 0xb200); + hws[IMX8MQ_CLK_ECSPI1] = imx8m_clk_hw_composite("ecspi1", imx8mq_ecspi1_sels, base + 0xb280); + hws[IMX8MQ_CLK_ECSPI2] = imx8m_clk_hw_composite("ecspi2", imx8mq_ecspi2_sels, base + 0xb300); + hws[IMX8MQ_CLK_PWM1] = imx8m_clk_hw_composite("pwm1", imx8mq_pwm1_sels, base + 0xb380); + hws[IMX8MQ_CLK_PWM2] = imx8m_clk_hw_composite("pwm2", imx8mq_pwm2_sels, base + 0xb400); + hws[IMX8MQ_CLK_PWM3] = imx8m_clk_hw_composite("pwm3", imx8mq_pwm3_sels, base + 0xb480); + hws[IMX8MQ_CLK_PWM4] = imx8m_clk_hw_composite("pwm4", imx8mq_pwm4_sels, base + 0xb500); + hws[IMX8MQ_CLK_GPT1] = imx8m_clk_hw_composite("gpt1", imx8mq_gpt1_sels, base + 0xb580); + hws[IMX8MQ_CLK_WDOG] = imx8m_clk_hw_composite("wdog", imx8mq_wdog_sels, base + 0xb900); + hws[IMX8MQ_CLK_WRCLK] = imx8m_clk_hw_composite("wrclk", imx8mq_wrclk_sels, base + 0xb980); + hws[IMX8MQ_CLK_CLKO1] = imx8m_clk_hw_composite("clko1", imx8mq_clko1_sels, base + 0xba00); + hws[IMX8MQ_CLK_CLKO2] = imx8m_clk_hw_composite("clko2", imx8mq_clko2_sels, base + 0xba80); + hws[IMX8MQ_CLK_DSI_CORE] = imx8m_clk_hw_composite("dsi_core", imx8mq_dsi_core_sels, base + 0xbb00); + hws[IMX8MQ_CLK_DSI_PHY_REF] = imx8m_clk_hw_composite("dsi_phy_ref", imx8mq_dsi_phy_sels, base + 0xbb80); + hws[IMX8MQ_CLK_DSI_DBI] = imx8m_clk_hw_composite("dsi_dbi", imx8mq_dsi_dbi_sels, base + 0xbc00); + hws[IMX8MQ_CLK_DSI_ESC] = imx8m_clk_hw_composite("dsi_esc", imx8mq_dsi_esc_sels, base + 0xbc80); + hws[IMX8MQ_CLK_DSI_AHB] = imx8m_clk_hw_composite("dsi_ahb", imx8mq_dsi_ahb_sels, base + 0x9200); + hws[IMX8MQ_CLK_DSI_IPG_DIV] = imx_clk_hw_divider2("dsi_ipg_div", "dsi_ahb", base + 0x9280, 0, 6); + hws[IMX8MQ_CLK_CSI1_CORE] = imx8m_clk_hw_composite("csi1_core", imx8mq_csi1_core_sels, base + 0xbd00); + hws[IMX8MQ_CLK_CSI1_PHY_REF] = imx8m_clk_hw_composite("csi1_phy_ref", imx8mq_csi1_phy_sels, base + 0xbd80); + hws[IMX8MQ_CLK_CSI1_ESC] = imx8m_clk_hw_composite("csi1_esc", imx8mq_csi1_esc_sels, base + 0xbe00); + hws[IMX8MQ_CLK_CSI2_CORE] = imx8m_clk_hw_composite("csi2_core", imx8mq_csi2_core_sels, base + 0xbe80); + hws[IMX8MQ_CLK_CSI2_PHY_REF] = imx8m_clk_hw_composite("csi2_phy_ref", imx8mq_csi2_phy_sels, base + 0xbf00); + hws[IMX8MQ_CLK_CSI2_ESC] = imx8m_clk_hw_composite("csi2_esc", imx8mq_csi2_esc_sels, base + 0xbf80); + hws[IMX8MQ_CLK_PCIE2_CTRL] = imx8m_clk_hw_composite("pcie2_ctrl", imx8mq_pcie2_ctrl_sels, base + 0xc000); + hws[IMX8MQ_CLK_PCIE2_PHY] = imx8m_clk_hw_composite("pcie2_phy", imx8mq_pcie2_phy_sels, base + 0xc080); + hws[IMX8MQ_CLK_PCIE2_AUX] = imx8m_clk_hw_composite("pcie2_aux", imx8mq_pcie2_aux_sels, base + 0xc100); + hws[IMX8MQ_CLK_ECSPI3] = imx8m_clk_hw_composite("ecspi3", imx8mq_ecspi3_sels, base + 0xc180); + + hws[IMX8MQ_CLK_ECSPI1_ROOT] = imx_clk_hw_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0); + hws[IMX8MQ_CLK_ECSPI2_ROOT] = imx_clk_hw_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0); + hws[IMX8MQ_CLK_ECSPI3_ROOT] = imx_clk_hw_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0); + hws[IMX8MQ_CLK_ENET1_ROOT] = imx_clk_hw_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0); + hws[IMX8MQ_CLK_GPIO1_ROOT] = imx_clk_hw_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0); + hws[IMX8MQ_CLK_GPIO2_ROOT] = imx_clk_hw_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0); + hws[IMX8MQ_CLK_GPIO3_ROOT] = imx_clk_hw_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0); + hws[IMX8MQ_CLK_GPIO4_ROOT] = imx_clk_hw_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0); + hws[IMX8MQ_CLK_GPIO5_ROOT] = imx_clk_hw_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0); + hws[IMX8MQ_CLK_GPT1_ROOT] = imx_clk_hw_gate4("gpt1_root_clk", "gpt1", base + 0x4100, 0); + hws[IMX8MQ_CLK_I2C1_ROOT] = imx_clk_hw_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0); + hws[IMX8MQ_CLK_I2C2_ROOT] = imx_clk_hw_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0); + hws[IMX8MQ_CLK_I2C3_ROOT] = imx_clk_hw_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0); + hws[IMX8MQ_CLK_I2C4_ROOT] = imx_clk_hw_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0); + hws[IMX8MQ_CLK_MU_ROOT] = imx_clk_hw_gate4("mu_root_clk", "ipg_root", base + 0x4210, 0); + hws[IMX8MQ_CLK_OCOTP_ROOT] = imx_clk_hw_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0); + hws[IMX8MQ_CLK_PCIE1_ROOT] = imx_clk_hw_gate4("pcie1_root_clk", "pcie1_ctrl", base + 0x4250, 0); + hws[IMX8MQ_CLK_PCIE2_ROOT] = imx_clk_hw_gate4("pcie2_root_clk", "pcie2_ctrl", base + 0x4640, 0); + hws[IMX8MQ_CLK_PWM1_ROOT] = imx_clk_hw_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0); + hws[IMX8MQ_CLK_PWM2_ROOT] = imx_clk_hw_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0); + hws[IMX8MQ_CLK_PWM3_ROOT] = imx_clk_hw_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0); + hws[IMX8MQ_CLK_PWM4_ROOT] = imx_clk_hw_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0); + hws[IMX8MQ_CLK_QSPI_ROOT] = imx_clk_hw_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0); + hws[IMX8MQ_CLK_RAWNAND_ROOT] = imx_clk_hw_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand); + hws[IMX8MQ_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_hw_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand); + hws[IMX8MQ_CLK_SAI1_ROOT] = imx_clk_hw_gate2_shared2("sai1_root_clk", "sai1", base + 0x4330, 0, &share_count_sai1); + hws[IMX8MQ_CLK_SAI1_IPG] = imx_clk_hw_gate2_shared2("sai1_ipg_clk", "ipg_audio_root", base + 0x4330, 0, &share_count_sai1); + hws[IMX8MQ_CLK_SAI2_ROOT] = imx_clk_hw_gate2_shared2("sai2_root_clk", "sai2", base + 0x4340, 0, &share_count_sai2); + hws[IMX8MQ_CLK_SAI2_IPG] = imx_clk_hw_gate2_shared2("sai2_ipg_clk", "ipg_root", base + 0x4340, 0, &share_count_sai2); + hws[IMX8MQ_CLK_SAI3_ROOT] = imx_clk_hw_gate2_shared2("sai3_root_clk", "sai3", base + 0x4350, 0, &share_count_sai3); + hws[IMX8MQ_CLK_SAI3_IPG] = imx_clk_hw_gate2_shared2("sai3_ipg_clk", "ipg_root", base + 0x4350, 0, &share_count_sai3); + hws[IMX8MQ_CLK_SAI4_ROOT] = imx_clk_hw_gate2_shared2("sai4_root_clk", "sai4", base + 0x4360, 0, &share_count_sai4); + hws[IMX8MQ_CLK_SAI4_IPG] = imx_clk_hw_gate2_shared2("sai4_ipg_clk", "ipg_audio_root", base + 0x4360, 0, &share_count_sai4); + hws[IMX8MQ_CLK_SAI5_ROOT] = imx_clk_hw_gate2_shared2("sai5_root_clk", "sai5", base + 0x4370, 0, &share_count_sai5); + hws[IMX8MQ_CLK_SAI5_IPG] = imx_clk_hw_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", base + 0x4370, 0, &share_count_sai5); + hws[IMX8MQ_CLK_SAI6_ROOT] = imx_clk_hw_gate2_shared2("sai6_root_clk", "sai6", base + 0x4380, 0, &share_count_sai6); + hws[IMX8MQ_CLK_SAI6_IPG] = imx_clk_hw_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", base + 0x4380, 0, &share_count_sai6); + hws[IMX8MQ_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", base + 0x4490, 0); + hws[IMX8MQ_CLK_UART2_ROOT] = imx_clk_hw_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0); + hws[IMX8MQ_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); + hws[IMX8MQ_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); + hws[IMX8MQ_CLK_USB1_CTRL_ROOT] = imx_clk_hw_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0); + hws[IMX8MQ_CLK_USB2_CTRL_ROOT] = imx_clk_hw_gate4("usb2_ctrl_root_clk", "usb_bus", base + 0x44e0, 0); + hws[IMX8MQ_CLK_USB1_PHY_ROOT] = imx_clk_hw_gate4("usb1_phy_root_clk", "usb_phy_ref", base + 0x44f0, 0); + hws[IMX8MQ_CLK_USB2_PHY_ROOT] = imx_clk_hw_gate4("usb2_phy_root_clk", "usb_phy_ref", base + 0x4500, 0); + hws[IMX8MQ_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); + hws[IMX8MQ_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); + hws[IMX8MQ_CLK_WDOG1_ROOT] = imx_clk_hw_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0); + hws[IMX8MQ_CLK_WDOG2_ROOT] = imx_clk_hw_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0); + hws[IMX8MQ_CLK_WDOG3_ROOT] = imx_clk_hw_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0); + hws[IMX8MQ_CLK_VPU_G1_ROOT] = imx_clk_hw_gate2_flags("vpu_g1_root_clk", "vpu_g1", base + 0x4560, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); + hws[IMX8MQ_CLK_GPU_ROOT] = imx_clk_hw_gate4("gpu_root_clk", "gpu_core", base + 0x4570, 0); + hws[IMX8MQ_CLK_VPU_G2_ROOT] = imx_clk_hw_gate2_flags("vpu_g2_root_clk", "vpu_g2", base + 0x45a0, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); + hws[IMX8MQ_CLK_DISP_ROOT] = imx_clk_hw_gate2_shared2("disp_root_clk", "disp_dc8000", base + 0x45d0, 0, &share_count_dcss); + hws[IMX8MQ_CLK_DISP_AXI_ROOT] = imx_clk_hw_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_dcss); + hws[IMX8MQ_CLK_DISP_APB_ROOT] = imx_clk_hw_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, &share_count_dcss); + hws[IMX8MQ_CLK_DISP_RTRM_ROOT] = imx_clk_hw_gate2_shared2("disp_rtrm_root_clk", "disp_rtrm", base + 0x45d0, 0, &share_count_dcss); + hws[IMX8MQ_CLK_TMU_ROOT] = imx_clk_hw_gate4("tmu_root_clk", "ipg_root", base + 0x4620, 0); + hws[IMX8MQ_CLK_VPU_DEC_ROOT] = imx_clk_hw_gate2_flags("vpu_dec_root_clk", "vpu_bus", base + 0x4630, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); + hws[IMX8MQ_CLK_CSI1_ROOT] = imx_clk_hw_gate4("csi1_root_clk", "csi1_core", base + 0x4650, 0); + hws[IMX8MQ_CLK_CSI2_ROOT] = imx_clk_hw_gate4("csi2_root_clk", "csi2_core", base + 0x4660, 0); + hws[IMX8MQ_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0); + hws[IMX8MQ_CLK_SDMA2_ROOT] = imx_clk_hw_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0); + + hws[IMX8MQ_GPT_3M_CLK] = imx_clk_hw_fixed_factor("gpt_3m", "osc_25m", 1, 8); + hws[IMX8MQ_CLK_DRAM_ALT_ROOT] = imx_clk_hw_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + + hws[IMX8MQ_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MQ_CLK_A53_CORE]->clk, + hws[IMX8MQ_CLK_A53_CORE]->clk, + hws[IMX8MQ_ARM_PLL_OUT]->clk, + hws[IMX8MQ_CLK_A53_DIV]->clk); + + imx_check_clk_hws(hws, IMX8MQ_CLK_END); + + err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + if (err < 0) { + dev_err(dev, "failed to register hws for i.MX8MQ\n"); + goto unregister_hws; + } + + imx_register_uart_clocks(4); + + return 0; + +unregister_hws: + imx_unregister_hw_clocks(hws, IMX8MQ_CLK_END); + + return err; +} + +static const struct of_device_id imx8mq_clk_of_match[] = { + { .compatible = "fsl,imx8mq-ccm" }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx8mq_clk_of_match); + + +static struct platform_driver imx8mq_clk_driver = { + .probe = imx8mq_clocks_probe, + .driver = { + .name = "imx8mq-ccm", + /* + * Disable bind attributes: clocks are not removed and + * reloading the driver will crash or break devices. + */ + .suppress_bind_attrs = true, + .of_match_table = imx8mq_clk_of_match, + }, +}; +module_platform_driver(imx8mq_clk_driver); +module_param(mcore_booted, bool, S_IRUGO); +MODULE_PARM_DESC(mcore_booted, "See Cortex-M core is booted or not"); + +MODULE_AUTHOR("Abel Vesa <abel.vesa@nxp.com>"); +MODULE_DESCRIPTION("NXP i.MX8MQ clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imx8qm-rsrc.c b/drivers/clk/imx/clk-imx8qm-rsrc.c new file mode 100644 index 000000000..87e0b6ac0 --- /dev/null +++ b/drivers/clk/imx/clk-imx8qm-rsrc.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019-2021 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <dt-bindings/firmware/imx/rsrc.h> + +#include "clk-scu.h" + +/* Keep sorted in the ascending order */ +static const u32 imx8qm_clk_scu_rsrc_table[] = { + IMX_SC_R_A53, + IMX_SC_R_A72, + IMX_SC_R_DC_0_VIDEO0, + IMX_SC_R_DC_0_VIDEO1, + IMX_SC_R_DC_0, + IMX_SC_R_DC_0_PLL_0, + IMX_SC_R_DC_0_PLL_1, + IMX_SC_R_DC_1_VIDEO0, + IMX_SC_R_DC_1_VIDEO1, + IMX_SC_R_DC_1, + IMX_SC_R_DC_1_PLL_0, + IMX_SC_R_DC_1_PLL_1, + IMX_SC_R_SPI_0, + IMX_SC_R_SPI_1, + IMX_SC_R_SPI_2, + IMX_SC_R_SPI_3, + IMX_SC_R_UART_0, + IMX_SC_R_UART_1, + IMX_SC_R_UART_2, + IMX_SC_R_UART_3, + IMX_SC_R_UART_4, + IMX_SC_R_EMVSIM_0, + IMX_SC_R_EMVSIM_1, + IMX_SC_R_I2C_0, + IMX_SC_R_I2C_1, + IMX_SC_R_I2C_2, + IMX_SC_R_I2C_3, + IMX_SC_R_I2C_4, + IMX_SC_R_ADC_0, + IMX_SC_R_ADC_1, + IMX_SC_R_FTM_0, + IMX_SC_R_FTM_1, + IMX_SC_R_CAN_0, + IMX_SC_R_GPU_0_PID0, + IMX_SC_R_GPU_1_PID0, + IMX_SC_R_PWM_0, + IMX_SC_R_PWM_1, + IMX_SC_R_PWM_2, + IMX_SC_R_PWM_3, + IMX_SC_R_PWM_4, + IMX_SC_R_PWM_5, + IMX_SC_R_PWM_6, + IMX_SC_R_PWM_7, + IMX_SC_R_GPT_0, + IMX_SC_R_GPT_1, + IMX_SC_R_GPT_2, + IMX_SC_R_GPT_3, + IMX_SC_R_GPT_4, + IMX_SC_R_FSPI_0, + IMX_SC_R_FSPI_1, + IMX_SC_R_SDHC_0, + IMX_SC_R_SDHC_1, + IMX_SC_R_SDHC_2, + IMX_SC_R_ENET_0, + IMX_SC_R_ENET_1, + IMX_SC_R_MLB_0, + IMX_SC_R_USB_2, + IMX_SC_R_NAND, + IMX_SC_R_LVDS_0, + IMX_SC_R_LVDS_0_PWM_0, + IMX_SC_R_LVDS_0_I2C_0, + IMX_SC_R_LVDS_0_I2C_1, + IMX_SC_R_LVDS_1, + IMX_SC_R_LVDS_1_PWM_0, + IMX_SC_R_LVDS_1_I2C_0, + IMX_SC_R_LVDS_1_I2C_1, + IMX_SC_R_M4_0_I2C, + IMX_SC_R_M4_1_I2C, + IMX_SC_R_AUDIO_PLL_0, + IMX_SC_R_VPU_UART, + IMX_SC_R_VPUCORE, + IMX_SC_R_MIPI_0, + IMX_SC_R_MIPI_0_PWM_0, + IMX_SC_R_MIPI_0_I2C_0, + IMX_SC_R_MIPI_0_I2C_1, + IMX_SC_R_MIPI_1, + IMX_SC_R_MIPI_1_PWM_0, + IMX_SC_R_MIPI_1_I2C_0, + IMX_SC_R_MIPI_1_I2C_1, + IMX_SC_R_CSI_0, + IMX_SC_R_CSI_0_PWM_0, + IMX_SC_R_CSI_0_I2C_0, + IMX_SC_R_CSI_1, + IMX_SC_R_CSI_1_PWM_0, + IMX_SC_R_CSI_1_I2C_0, + IMX_SC_R_HDMI, + IMX_SC_R_HDMI_I2S, + IMX_SC_R_HDMI_I2C_0, + IMX_SC_R_HDMI_PLL_0, + IMX_SC_R_HDMI_RX, + IMX_SC_R_HDMI_RX_BYPASS, + IMX_SC_R_HDMI_RX_I2C_0, + IMX_SC_R_AUDIO_PLL_1, + IMX_SC_R_AUDIO_CLK_0, + IMX_SC_R_AUDIO_CLK_1, + IMX_SC_R_HDMI_RX_PWM_0, + IMX_SC_R_HDMI_PLL_1, + IMX_SC_R_VPU, +}; + +const struct imx_clk_scu_rsrc_table imx_clk_scu_rsrc_imx8qm = { + .rsrc = imx8qm_clk_scu_rsrc_table, + .num = ARRAY_SIZE(imx8qm_clk_scu_rsrc_table), +}; diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.c b/drivers/clk/imx/clk-imx8qxp-lpcg.c new file mode 100644 index 000000000..5e31a6a24 --- /dev/null +++ b/drivers/clk/imx/clk-imx8qxp-lpcg.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include "clk-scu.h" +#include "clk-imx8qxp-lpcg.h" + +#include <dt-bindings/clock/imx8-clock.h> + +/* + * struct imx8qxp_lpcg_data - Description of one LPCG clock + * @id: clock ID + * @name: clock name + * @parent: parent clock name + * @flags: common clock flags + * @offset: offset of this LPCG clock + * @bit_idx: bit index of this LPCG clock + * @hw_gate: whether supports HW autogate + * + * This structure describes one LPCG clock + */ +struct imx8qxp_lpcg_data { + int id; + char *name; + char *parent; + unsigned long flags; + u32 offset; + u8 bit_idx; + bool hw_gate; +}; + +/* + * struct imx8qxp_ss_lpcg - Description of one subsystem LPCG clocks + * @lpcg: LPCG clocks array of one subsystem + * @num_lpcg: the number of LPCG clocks + * @num_max: the maximum number of LPCG clocks + * + * This structure describes each subsystem LPCG clocks information + * which then will be used to create respective LPCGs clocks + */ +struct imx8qxp_ss_lpcg { + const struct imx8qxp_lpcg_data *lpcg; + u8 num_lpcg; + u8 num_max; +}; + +static const struct imx8qxp_lpcg_data imx8qxp_lpcg_adma[] = { + { IMX_ADMA_LPCG_UART0_IPG_CLK, "uart0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_0_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_UART0_BAUD_CLK, "uart0_lpcg_baud_clk", "uart0_clk", 0, ADMA_LPUART_0_LPCG, 0, 0, }, + { IMX_ADMA_LPCG_UART1_IPG_CLK, "uart1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_1_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_UART1_BAUD_CLK, "uart1_lpcg_baud_clk", "uart1_clk", 0, ADMA_LPUART_1_LPCG, 0, 0, }, + { IMX_ADMA_LPCG_UART2_IPG_CLK, "uart2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_2_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_UART2_BAUD_CLK, "uart2_lpcg_baud_clk", "uart2_clk", 0, ADMA_LPUART_2_LPCG, 0, 0, }, + { IMX_ADMA_LPCG_UART3_IPG_CLK, "uart3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPUART_3_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_UART3_BAUD_CLK, "uart3_lpcg_baud_clk", "uart3_clk", 0, ADMA_LPUART_3_LPCG, 0, 0, }, + { IMX_ADMA_LPCG_I2C0_IPG_CLK, "i2c0_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_0_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_I2C0_CLK, "i2c0_lpcg_clk", "i2c0_clk", 0, ADMA_LPI2C_0_LPCG, 0, 0, }, + { IMX_ADMA_LPCG_I2C1_IPG_CLK, "i2c1_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_1_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_I2C1_CLK, "i2c1_lpcg_clk", "i2c1_clk", 0, ADMA_LPI2C_1_LPCG, 0, 0, }, + { IMX_ADMA_LPCG_I2C2_IPG_CLK, "i2c2_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_2_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_I2C2_CLK, "i2c2_lpcg_clk", "i2c2_clk", 0, ADMA_LPI2C_2_LPCG, 0, 0, }, + { IMX_ADMA_LPCG_I2C3_IPG_CLK, "i2c3_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_LPI2C_3_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_I2C3_CLK, "i2c3_lpcg_clk", "i2c3_clk", 0, ADMA_LPI2C_3_LPCG, 0, 0, }, + + { IMX_ADMA_LPCG_DSP_CORE_CLK, "dsp_lpcg_core_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 28, 0, }, + { IMX_ADMA_LPCG_DSP_IPG_CLK, "dsp_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 20, 0, }, + { IMX_ADMA_LPCG_DSP_ADB_CLK, "dsp_lpcg_adb_clk", "dma_ipg_clk_root", 0, ADMA_HIFI_LPCG, 16, 0, }, + { IMX_ADMA_LPCG_OCRAM_IPG_CLK, "ocram_lpcg_ipg_clk", "dma_ipg_clk_root", 0, ADMA_OCRAM_LPCG, 16, 0, }, +}; + +static const struct imx8qxp_ss_lpcg imx8qxp_ss_adma = { + .lpcg = imx8qxp_lpcg_adma, + .num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_adma), + .num_max = IMX_ADMA_LPCG_CLK_END, +}; + +static const struct imx8qxp_lpcg_data imx8qxp_lpcg_conn[] = { + { IMX_CONN_LPCG_SDHC0_PER_CLK, "sdhc0_lpcg_per_clk", "sdhc0_clk", 0, CONN_USDHC_0_LPCG, 0, 0, }, + { IMX_CONN_LPCG_SDHC0_IPG_CLK, "sdhc0_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_0_LPCG, 16, 0, }, + { IMX_CONN_LPCG_SDHC0_HCLK, "sdhc0_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_0_LPCG, 20, 0, }, + { IMX_CONN_LPCG_SDHC1_PER_CLK, "sdhc1_lpcg_per_clk", "sdhc1_clk", 0, CONN_USDHC_1_LPCG, 0, 0, }, + { IMX_CONN_LPCG_SDHC1_IPG_CLK, "sdhc1_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_1_LPCG, 16, 0, }, + { IMX_CONN_LPCG_SDHC1_HCLK, "sdhc1_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_1_LPCG, 20, 0, }, + { IMX_CONN_LPCG_SDHC2_PER_CLK, "sdhc2_lpcg_per_clk", "sdhc2_clk", 0, CONN_USDHC_2_LPCG, 0, 0, }, + { IMX_CONN_LPCG_SDHC2_IPG_CLK, "sdhc2_lpcg_ipg_clk", "conn_ipg_clk_root", 0, CONN_USDHC_2_LPCG, 16, 0, }, + { IMX_CONN_LPCG_SDHC2_HCLK, "sdhc2_lpcg_ahb_clk", "conn_axi_clk_root", 0, CONN_USDHC_2_LPCG, 20, 0, }, + { IMX_CONN_LPCG_ENET0_ROOT_CLK, "enet0_ipg_root_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 0, 0, }, + { IMX_CONN_LPCG_ENET0_TX_CLK, "enet0_tx_clk", "enet0_clk", 0, CONN_ENET_0_LPCG, 4, 0, }, + { IMX_CONN_LPCG_ENET0_AHB_CLK, "enet0_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_0_LPCG, 8, 0, }, + { IMX_CONN_LPCG_ENET0_IPG_S_CLK, "enet0_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_0_LPCG, 20, 0, }, + { IMX_CONN_LPCG_ENET0_IPG_CLK, "enet0_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_0_LPCG, 16, 0, }, + { IMX_CONN_LPCG_ENET1_ROOT_CLK, "enet1_ipg_root_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 0, 0, }, + { IMX_CONN_LPCG_ENET1_TX_CLK, "enet1_tx_clk", "enet1_clk", 0, CONN_ENET_1_LPCG, 4, 0, }, + { IMX_CONN_LPCG_ENET1_AHB_CLK, "enet1_ahb_clk", "conn_axi_clk_root", 0, CONN_ENET_1_LPCG, 8, 0, }, + { IMX_CONN_LPCG_ENET1_IPG_S_CLK, "enet1_ipg_s_clk", "conn_ipg_clk_root", 0, CONN_ENET_1_LPCG, 20, 0, }, + { IMX_CONN_LPCG_ENET1_IPG_CLK, "enet1_ipg_clk", "enet0_ipg_s_clk", 0, CONN_ENET_1_LPCG, 16, 0, }, +}; + +static const struct imx8qxp_ss_lpcg imx8qxp_ss_conn = { + .lpcg = imx8qxp_lpcg_conn, + .num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_conn), + .num_max = IMX_CONN_LPCG_CLK_END, +}; + +static const struct imx8qxp_lpcg_data imx8qxp_lpcg_lsio[] = { + { IMX_LSIO_LPCG_PWM0_IPG_CLK, "pwm0_lpcg_ipg_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 0, 0, }, + { IMX_LSIO_LPCG_PWM0_IPG_HF_CLK, "pwm0_lpcg_ipg_hf_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 4, 0, }, + { IMX_LSIO_LPCG_PWM0_IPG_S_CLK, "pwm0_lpcg_ipg_s_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 16, 0, }, + { IMX_LSIO_LPCG_PWM0_IPG_SLV_CLK, "pwm0_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_0_LPCG, 20, 0, }, + { IMX_LSIO_LPCG_PWM0_IPG_MSTR_CLK, "pwm0_lpcg_ipg_mstr_clk", "pwm0_clk", 0, LSIO_PWM_0_LPCG, 24, 0, }, + { IMX_LSIO_LPCG_PWM1_IPG_CLK, "pwm1_lpcg_ipg_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 0, 0, }, + { IMX_LSIO_LPCG_PWM1_IPG_HF_CLK, "pwm1_lpcg_ipg_hf_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 4, 0, }, + { IMX_LSIO_LPCG_PWM1_IPG_S_CLK, "pwm1_lpcg_ipg_s_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 16, 0, }, + { IMX_LSIO_LPCG_PWM1_IPG_SLV_CLK, "pwm1_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_1_LPCG, 20, 0, }, + { IMX_LSIO_LPCG_PWM1_IPG_MSTR_CLK, "pwm1_lpcg_ipg_mstr_clk", "pwm1_clk", 0, LSIO_PWM_1_LPCG, 24, 0, }, + { IMX_LSIO_LPCG_PWM2_IPG_CLK, "pwm2_lpcg_ipg_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 0, 0, }, + { IMX_LSIO_LPCG_PWM2_IPG_HF_CLK, "pwm2_lpcg_ipg_hf_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 4, 0, }, + { IMX_LSIO_LPCG_PWM2_IPG_S_CLK, "pwm2_lpcg_ipg_s_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 16, 0, }, + { IMX_LSIO_LPCG_PWM2_IPG_SLV_CLK, "pwm2_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_2_LPCG, 20, 0, }, + { IMX_LSIO_LPCG_PWM2_IPG_MSTR_CLK, "pwm2_lpcg_ipg_mstr_clk", "pwm2_clk", 0, LSIO_PWM_2_LPCG, 24, 0, }, + { IMX_LSIO_LPCG_PWM3_IPG_CLK, "pwm3_lpcg_ipg_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 0, 0, }, + { IMX_LSIO_LPCG_PWM3_IPG_HF_CLK, "pwm3_lpcg_ipg_hf_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 4, 0, }, + { IMX_LSIO_LPCG_PWM3_IPG_S_CLK, "pwm3_lpcg_ipg_s_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 16, 0, }, + { IMX_LSIO_LPCG_PWM3_IPG_SLV_CLK, "pwm3_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_3_LPCG, 20, 0, }, + { IMX_LSIO_LPCG_PWM3_IPG_MSTR_CLK, "pwm3_lpcg_ipg_mstr_clk", "pwm3_clk", 0, LSIO_PWM_3_LPCG, 24, 0, }, + { IMX_LSIO_LPCG_PWM4_IPG_CLK, "pwm4_lpcg_ipg_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 0, 0, }, + { IMX_LSIO_LPCG_PWM4_IPG_HF_CLK, "pwm4_lpcg_ipg_hf_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 4, 0, }, + { IMX_LSIO_LPCG_PWM4_IPG_S_CLK, "pwm4_lpcg_ipg_s_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 16, 0, }, + { IMX_LSIO_LPCG_PWM4_IPG_SLV_CLK, "pwm4_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_4_LPCG, 20, 0, }, + { IMX_LSIO_LPCG_PWM4_IPG_MSTR_CLK, "pwm4_lpcg_ipg_mstr_clk", "pwm4_clk", 0, LSIO_PWM_4_LPCG, 24, 0, }, + { IMX_LSIO_LPCG_PWM5_IPG_CLK, "pwm5_lpcg_ipg_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 0, 0, }, + { IMX_LSIO_LPCG_PWM5_IPG_HF_CLK, "pwm5_lpcg_ipg_hf_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 4, 0, }, + { IMX_LSIO_LPCG_PWM5_IPG_S_CLK, "pwm5_lpcg_ipg_s_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 16, 0, }, + { IMX_LSIO_LPCG_PWM5_IPG_SLV_CLK, "pwm5_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_5_LPCG, 20, 0, }, + { IMX_LSIO_LPCG_PWM5_IPG_MSTR_CLK, "pwm5_lpcg_ipg_mstr_clk", "pwm5_clk", 0, LSIO_PWM_5_LPCG, 24, 0, }, + { IMX_LSIO_LPCG_PWM6_IPG_CLK, "pwm6_lpcg_ipg_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 0, 0, }, + { IMX_LSIO_LPCG_PWM6_IPG_HF_CLK, "pwm6_lpcg_ipg_hf_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 4, 0, }, + { IMX_LSIO_LPCG_PWM6_IPG_S_CLK, "pwm6_lpcg_ipg_s_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 16, 0, }, + { IMX_LSIO_LPCG_PWM6_IPG_SLV_CLK, "pwm6_lpcg_ipg_slv_clk", "lsio_bus_clk_root", 0, LSIO_PWM_6_LPCG, 20, 0, }, + { IMX_LSIO_LPCG_PWM6_IPG_MSTR_CLK, "pwm6_lpcg_ipg_mstr_clk", "pwm6_clk", 0, LSIO_PWM_6_LPCG, 24, 0, }, +}; + +static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = { + .lpcg = imx8qxp_lpcg_lsio, + .num_lpcg = ARRAY_SIZE(imx8qxp_lpcg_lsio), + .num_max = IMX_LSIO_LPCG_CLK_END, +}; + +#define IMX_LPCG_MAX_CLKS 8 + +static struct clk_hw *imx_lpcg_of_clk_src_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_hw_onecell_data *hw_data = data; + unsigned int idx = clkspec->args[0] / 4; + + if (idx >= hw_data->num) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return hw_data->hws[idx]; +} + +static int imx_lpcg_parse_clks_from_dt(struct platform_device *pdev, + struct device_node *np) +{ + const char *output_names[IMX_LPCG_MAX_CLKS]; + const char *parent_names[IMX_LPCG_MAX_CLKS]; + unsigned int bit_offset[IMX_LPCG_MAX_CLKS]; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clk_hws; + struct resource *res; + void __iomem *base; + int count; + int idx; + int ret; + int i; + + if (!of_device_is_compatible(np, "fsl,imx8qxp-lpcg")) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + count = of_property_count_u32_elems(np, "clock-indices"); + if (count < 0) { + dev_err(&pdev->dev, "failed to count clocks\n"); + return -EINVAL; + } + + /* + * A trick here is that we set the num of clks to the MAX instead + * of the count from clock-indices because one LPCG supports up to + * 8 clock outputs which each of them is fixed to 4 bits. Then we can + * easily get the clock by clk-indices (bit-offset) / 4. + * And the cost is very limited few pointers. + */ + + clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws, + IMX_LPCG_MAX_CLKS), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX_LPCG_MAX_CLKS; + clk_hws = clk_data->hws; + + ret = of_property_read_u32_array(np, "clock-indices", bit_offset, + count); + if (ret < 0) { + dev_err(&pdev->dev, "failed to read clock-indices\n"); + return -EINVAL; + } + + ret = of_clk_parent_fill(np, parent_names, count); + if (ret != count) { + dev_err(&pdev->dev, "failed to get clock parent names\n"); + return count; + } + + ret = of_property_read_string_array(np, "clock-output-names", + output_names, count); + if (ret != count) { + dev_err(&pdev->dev, "failed to read clock-output-names\n"); + return -EINVAL; + } + + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 500); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + for (i = 0; i < count; i++) { + idx = bit_offset[i] / 4; + if (idx >= IMX_LPCG_MAX_CLKS) { + dev_warn(&pdev->dev, "invalid bit offset of clock %d\n", + i); + ret = -EINVAL; + goto unreg; + } + + clk_hws[idx] = imx_clk_lpcg_scu_dev(&pdev->dev, output_names[i], + parent_names[i], 0, base, + bit_offset[i], false); + if (IS_ERR(clk_hws[idx])) { + dev_warn(&pdev->dev, "failed to register clock %d\n", + idx); + ret = PTR_ERR(clk_hws[idx]); + goto unreg; + } + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, imx_lpcg_of_clk_src_get, + clk_data); + if (ret) + goto unreg; + + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + + return 0; + +unreg: + while (--i >= 0) { + idx = bit_offset[i] / 4; + if (clk_hws[idx]) + imx_clk_lpcg_scu_unregister(clk_hws[idx]); + } + + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int imx8qxp_lpcg_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct clk_hw_onecell_data *clk_data; + const struct imx8qxp_ss_lpcg *ss_lpcg; + const struct imx8qxp_lpcg_data *lpcg; + struct resource *res; + struct clk_hw **clks; + void __iomem *base; + int ret; + int i; + + /* try new binding to parse clocks from device tree first */ + ret = imx_lpcg_parse_clks_from_dt(pdev, np); + if (!ret) + return 0; + + ss_lpcg = of_device_get_match_data(dev); + if (!ss_lpcg) + return -ENODEV; + + /* + * Please don't replace this with devm_platform_ioremap_resource. + * + * devm_platform_ioremap_resource calls devm_ioremap_resource which + * differs from devm_ioremap by also calling devm_request_mem_region + * and preventing other mappings in the same area. + * + * On imx8 the LPCG nodes map entire subsystems and overlap + * peripherals, this means that using devm_platform_ioremap_resource + * will cause many devices to fail to probe including serial ports. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + base = devm_ioremap(dev, res->start, resource_size(res)); + if (!base) + return -ENOMEM; + + clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hws, + ss_lpcg->num_max), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = ss_lpcg->num_max; + clks = clk_data->hws; + + for (i = 0; i < ss_lpcg->num_lpcg; i++) { + lpcg = ss_lpcg->lpcg + i; + clks[lpcg->id] = imx_clk_lpcg_scu(lpcg->name, lpcg->parent, + lpcg->flags, base + lpcg->offset, + lpcg->bit_idx, lpcg->hw_gate); + } + + for (i = 0; i < clk_data->num; i++) { + if (IS_ERR(clks[i])) + pr_warn("i.MX clk %u: register failed with %ld\n", + i, PTR_ERR(clks[i])); + } + + return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); +} + +static const struct of_device_id imx8qxp_lpcg_match[] = { + { .compatible = "fsl,imx8qxp-lpcg-adma", &imx8qxp_ss_adma, }, + { .compatible = "fsl,imx8qxp-lpcg-conn", &imx8qxp_ss_conn, }, + { .compatible = "fsl,imx8qxp-lpcg-lsio", &imx8qxp_ss_lsio, }, + { .compatible = "fsl,imx8qxp-lpcg", NULL }, + { /* sentinel */ } +}; + +static struct platform_driver imx8qxp_lpcg_clk_driver = { + .driver = { + .name = "imx8qxp-lpcg-clk", + .of_match_table = imx8qxp_lpcg_match, + .pm = &imx_clk_lpcg_scu_pm_ops, + .suppress_bind_attrs = true, + }, + .probe = imx8qxp_lpcg_clk_probe, +}; + +module_platform_driver(imx8qxp_lpcg_clk_driver); + +MODULE_AUTHOR("Aisheng Dong <aisheng.dong@nxp.com>"); +MODULE_DESCRIPTION("NXP i.MX8QXP LPCG clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.h b/drivers/clk/imx/clk-imx8qxp-lpcg.h new file mode 100644 index 000000000..2a37ce57c --- /dev/null +++ b/drivers/clk/imx/clk-imx8qxp-lpcg.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#ifndef _IMX8QXP_LPCG_H +#define _IMX8QXP_LPCG_H + +/*LSIO SS */ +#define LSIO_PWM_0_LPCG 0x00000 +#define LSIO_PWM_1_LPCG 0x10000 +#define LSIO_PWM_2_LPCG 0x20000 +#define LSIO_PWM_3_LPCG 0x30000 +#define LSIO_PWM_4_LPCG 0x40000 +#define LSIO_PWM_5_LPCG 0x50000 +#define LSIO_PWM_6_LPCG 0x60000 +#define LSIO_PWM_7_LPCG 0x70000 +#define LSIO_GPIO_0_LPCG 0x80000 +#define LSIO_GPIO_1_LPCG 0x90000 +#define LSIO_GPIO_2_LPCG 0xa0000 +#define LSIO_GPIO_3_LPCG 0xb0000 +#define LSIO_GPIO_4_LPCG 0xc0000 +#define LSIO_GPIO_5_LPCG 0xd0000 +#define LSIO_GPIO_6_LPCG 0xe0000 +#define LSIO_GPIO_7_LPCG 0xf0000 +#define LSIO_FSPI_0_LPCG 0x120000 +#define LSIO_FSPI_1_LPCG 0x130000 +#define LSIO_GPT_0_LPCG 0x140000 +#define LSIO_GPT_1_LPCG 0x150000 +#define LSIO_GPT_2_LPCG 0x160000 +#define LSIO_GPT_3_LPCG 0x170000 +#define LSIO_GPT_4_LPCG 0x180000 +#define LSIO_OCRAM_LPCG 0x190000 +#define LSIO_KPP_LPCG 0x1a0000 +#define LSIO_ROMCP_LPCG 0x100000 + +/* Connectivity SS */ +#define CONN_USDHC_0_LPCG 0x00000 +#define CONN_USDHC_1_LPCG 0x10000 +#define CONN_USDHC_2_LPCG 0x20000 +#define CONN_ENET_0_LPCG 0x30000 +#define CONN_ENET_1_LPCG 0x40000 +#define CONN_DTCP_LPCG 0x50000 +#define CONN_MLB_LPCG 0x60000 +#define CONN_USB_2_LPCG 0x70000 +#define CONN_USB_3_LPCG 0x80000 +#define CONN_NAND_LPCG 0x90000 +#define CONN_EDMA_LPCG 0xa0000 + +/* ADMA SS */ +#define ADMA_ASRC_0_LPCG 0x400000 +#define ADMA_ESAI_0_LPCG 0x410000 +#define ADMA_SPDIF_0_LPCG 0x420000 +#define ADMA_SAI_0_LPCG 0x440000 +#define ADMA_SAI_1_LPCG 0x450000 +#define ADMA_SAI_2_LPCG 0x460000 +#define ADMA_SAI_3_LPCG 0x470000 +#define ADMA_GPT_5_LPCG 0x4b0000 +#define ADMA_GPT_6_LPCG 0x4c0000 +#define ADMA_GPT_7_LPCG 0x4d0000 +#define ADMA_GPT_8_LPCG 0x4e0000 +#define ADMA_GPT_9_LPCG 0x4f0000 +#define ADMA_GPT_10_LPCG 0x500000 +#define ADMA_HIFI_LPCG 0x580000 +#define ADMA_OCRAM_LPCG 0x590000 +#define ADMA_EDMA_0_LPCG 0x5f0000 +#define ADMA_ASRC_1_LPCG 0xc00000 +#define ADMA_SAI_4_LPCG 0xc20000 +#define ADMA_SAI_5_LPCG 0xc30000 +#define ADMA_AMIX_LPCG 0xc40000 +#define ADMA_MQS_LPCG 0xc50000 +#define ADMA_ACM_LPCG 0xc60000 +#define ADMA_REC_CLK0_LPCG 0xd00000 +#define ADMA_REC_CLK1_LPCG 0xd10000 +#define ADMA_PLL_CLK0_LPCG 0xd20000 +#define ADMA_PLL_CLK1_LPCG 0xd30000 +#define ADMA_MCLKOUT0_LPCG 0xd50000 +#define ADMA_MCLKOUT1_LPCG 0xd60000 +#define ADMA_EDMA_1_LPCG 0xdf0000 +#define ADMA_LPSPI_0_LPCG 0x1400000 +#define ADMA_LPSPI_1_LPCG 0x1410000 +#define ADMA_LPSPI_2_LPCG 0x1420000 +#define ADMA_LPSPI_3_LPCG 0x1430000 +#define ADMA_LPUART_0_LPCG 0x1460000 +#define ADMA_LPUART_1_LPCG 0x1470000 +#define ADMA_LPUART_2_LPCG 0x1480000 +#define ADMA_LPUART_3_LPCG 0x1490000 +#define ADMA_LCD_LPCG 0x1580000 +#define ADMA_PWM_LPCG 0x1590000 +#define ADMA_LPI2C_0_LPCG 0x1c00000 +#define ADMA_LPI2C_1_LPCG 0x1c10000 +#define ADMA_LPI2C_2_LPCG 0x1c20000 +#define ADMA_LPI2C_3_LPCG 0x1c30000 +#define ADMA_ADC_0_LPCG 0x1c80000 +#define ADMA_FTM_0_LPCG 0x1ca0000 +#define ADMA_FTM_1_LPCG 0x1cb0000 +#define ADMA_FLEXCAN_0_LPCG 0x1cd0000 +#define ADMA_FLEXCAN_1_LPCG 0x1ce0000 +#define ADMA_FLEXCAN_2_LPCG 0x1cf0000 + +#endif /* _IMX8QXP_LPCG_H */ diff --git a/drivers/clk/imx/clk-imx8qxp-rsrc.c b/drivers/clk/imx/clk-imx8qxp-rsrc.c new file mode 100644 index 000000000..df09f2a79 --- /dev/null +++ b/drivers/clk/imx/clk-imx8qxp-rsrc.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019-2021 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <dt-bindings/firmware/imx/rsrc.h> + +#include "clk-scu.h" + +/* Keep sorted in the ascending order */ +static const u32 imx8qxp_clk_scu_rsrc_table[] = { + IMX_SC_R_DC_0_VIDEO0, + IMX_SC_R_DC_0_VIDEO1, + IMX_SC_R_DC_0, + IMX_SC_R_DC_0_PLL_0, + IMX_SC_R_DC_0_PLL_1, + IMX_SC_R_SPI_0, + IMX_SC_R_SPI_1, + IMX_SC_R_SPI_2, + IMX_SC_R_SPI_3, + IMX_SC_R_UART_0, + IMX_SC_R_UART_1, + IMX_SC_R_UART_2, + IMX_SC_R_UART_3, + IMX_SC_R_I2C_0, + IMX_SC_R_I2C_1, + IMX_SC_R_I2C_2, + IMX_SC_R_I2C_3, + IMX_SC_R_ADC_0, + IMX_SC_R_FTM_0, + IMX_SC_R_FTM_1, + IMX_SC_R_CAN_0, + IMX_SC_R_GPU_0_PID0, + IMX_SC_R_LCD_0, + IMX_SC_R_LCD_0_PWM_0, + IMX_SC_R_PWM_0, + IMX_SC_R_PWM_1, + IMX_SC_R_PWM_2, + IMX_SC_R_PWM_3, + IMX_SC_R_PWM_4, + IMX_SC_R_PWM_5, + IMX_SC_R_PWM_6, + IMX_SC_R_PWM_7, + IMX_SC_R_GPT_0, + IMX_SC_R_GPT_1, + IMX_SC_R_GPT_2, + IMX_SC_R_GPT_3, + IMX_SC_R_GPT_4, + IMX_SC_R_FSPI_0, + IMX_SC_R_FSPI_1, + IMX_SC_R_SDHC_0, + IMX_SC_R_SDHC_1, + IMX_SC_R_SDHC_2, + IMX_SC_R_ENET_0, + IMX_SC_R_ENET_1, + IMX_SC_R_MLB_0, + IMX_SC_R_USB_2, + IMX_SC_R_NAND, + IMX_SC_R_LVDS_0, + IMX_SC_R_LVDS_1, + IMX_SC_R_M4_0_I2C, + IMX_SC_R_ELCDIF_PLL, + IMX_SC_R_AUDIO_PLL_0, + IMX_SC_R_PI_0, + IMX_SC_R_PI_0_PLL, + IMX_SC_R_MIPI_0, + IMX_SC_R_MIPI_0_PWM_0, + IMX_SC_R_MIPI_0_I2C_0, + IMX_SC_R_MIPI_0_I2C_1, + IMX_SC_R_MIPI_1, + IMX_SC_R_MIPI_1_PWM_0, + IMX_SC_R_MIPI_1_I2C_0, + IMX_SC_R_MIPI_1_I2C_1, + IMX_SC_R_CSI_0, + IMX_SC_R_CSI_0_PWM_0, + IMX_SC_R_CSI_0_I2C_0, + IMX_SC_R_AUDIO_PLL_1, + IMX_SC_R_AUDIO_CLK_0, + IMX_SC_R_AUDIO_CLK_1, + IMX_SC_R_A35, + IMX_SC_R_VPU_DEC_0, + IMX_SC_R_VPU_ENC_0, +}; + +const struct imx_clk_scu_rsrc_table imx_clk_scu_rsrc_imx8qxp = { + .rsrc = imx8qxp_clk_scu_rsrc_table, + .num = ARRAY_SIZE(imx8qxp_clk_scu_rsrc_table), +}; diff --git a/drivers/clk/imx/clk-imx8qxp.c b/drivers/clk/imx/clk-imx8qxp.c new file mode 100644 index 000000000..273de1f29 --- /dev/null +++ b/drivers/clk/imx/clk-imx8qxp.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018-2021 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "clk-scu.h" + +#include <dt-bindings/firmware/imx/rsrc.h> + +static const char *dc0_sels[] = { + "clk_dummy", + "clk_dummy", + "dc0_pll0_clk", + "dc0_pll1_clk", + "dc0_bypass0_clk", +}; + +static const char * const dc1_sels[] = { + "clk_dummy", + "clk_dummy", + "dc1_pll0_clk", + "dc1_pll1_clk", + "dc1_bypass0_clk", +}; + +static const char * const enet0_rgmii_txc_sels[] = { + "enet0_ref_div", + "clk_dummy", +}; + +static const char * const enet1_rgmii_txc_sels[] = { + "enet1_ref_div", + "clk_dummy", +}; + +static const char * const hdmi_sels[] = { + "clk_dummy", + "hdmi_dig_pll_clk", + "clk_dummy", + "clk_dummy", + "hdmi_av_pll_clk", +}; + +static const char * const hdmi_rx_sels[] = { + "clk_dummy", + "hdmi_rx_dig_pll_clk", + "clk_dummy", + "clk_dummy", + "hdmi_rx_bypass_clk", +}; + +static const char * const lcd_pxl_sels[] = { + "clk_dummy", + "clk_dummy", + "clk_dummy", + "clk_dummy", + "lcd_pxl_bypass_div_clk", +}; + +static const char * const mipi_sels[] = { + "clk_dummy", + "clk_dummy", + "mipi_pll_div2_clk", + "clk_dummy", + "clk_dummy", +}; + +static const char * const lcd_sels[] = { + "clk_dummy", + "clk_dummy", + "clk_dummy", + "clk_dummy", + "elcdif_pll", +}; + +static const char * const pi_pll0_sels[] = { + "clk_dummy", + "pi_dpll_clk", + "clk_dummy", + "clk_dummy", + "clk_dummy", +}; + +static int imx8qxp_clk_probe(struct platform_device *pdev) +{ + struct device_node *ccm_node = pdev->dev.of_node; + const struct imx_clk_scu_rsrc_table *rsrc_table; + int ret; + + rsrc_table = of_device_get_match_data(&pdev->dev); + ret = imx_clk_scu_init(ccm_node, rsrc_table); + if (ret) + return ret; + + /* ARM core */ + imx_clk_scu("a35_clk", IMX_SC_R_A35, IMX_SC_PM_CLK_CPU); + imx_clk_scu("a53_clk", IMX_SC_R_A53, IMX_SC_PM_CLK_CPU); + imx_clk_scu("a72_clk", IMX_SC_R_A72, IMX_SC_PM_CLK_CPU); + + /* LSIO SS */ + imx_clk_scu("pwm0_clk", IMX_SC_R_PWM_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm1_clk", IMX_SC_R_PWM_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm2_clk", IMX_SC_R_PWM_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm3_clk", IMX_SC_R_PWM_3, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm4_clk", IMX_SC_R_PWM_4, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm5_clk", IMX_SC_R_PWM_5, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm6_clk", IMX_SC_R_PWM_6, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm7_clk", IMX_SC_R_PWM_7, IMX_SC_PM_CLK_PER); + imx_clk_scu("gpt0_clk", IMX_SC_R_GPT_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("gpt1_clk", IMX_SC_R_GPT_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("gpt2_clk", IMX_SC_R_GPT_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("gpt3_clk", IMX_SC_R_GPT_3, IMX_SC_PM_CLK_PER); + imx_clk_scu("gpt4_clk", IMX_SC_R_GPT_4, IMX_SC_PM_CLK_PER); + imx_clk_scu("fspi0_clk", IMX_SC_R_FSPI_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("fspi1_clk", IMX_SC_R_FSPI_1, IMX_SC_PM_CLK_PER); + + /* DMA SS */ + imx_clk_scu("uart0_clk", IMX_SC_R_UART_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("uart1_clk", IMX_SC_R_UART_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("uart2_clk", IMX_SC_R_UART_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("uart3_clk", IMX_SC_R_UART_3, IMX_SC_PM_CLK_PER); + imx_clk_scu("uart4_clk", IMX_SC_R_UART_4, IMX_SC_PM_CLK_PER); + imx_clk_scu("sim0_clk", IMX_SC_R_EMVSIM_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("spi0_clk", IMX_SC_R_SPI_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("spi1_clk", IMX_SC_R_SPI_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("spi2_clk", IMX_SC_R_SPI_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("spi3_clk", IMX_SC_R_SPI_3, IMX_SC_PM_CLK_PER); + imx_clk_scu("can0_clk", IMX_SC_R_CAN_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("can1_clk", IMX_SC_R_CAN_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("can2_clk", IMX_SC_R_CAN_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("i2c0_clk", IMX_SC_R_I2C_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("i2c1_clk", IMX_SC_R_I2C_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("i2c2_clk", IMX_SC_R_I2C_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("i2c3_clk", IMX_SC_R_I2C_3, IMX_SC_PM_CLK_PER); + imx_clk_scu("i2c4_clk", IMX_SC_R_I2C_4, IMX_SC_PM_CLK_PER); + imx_clk_scu("ftm0_clk", IMX_SC_R_FTM_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("ftm1_clk", IMX_SC_R_FTM_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("adc0_clk", IMX_SC_R_ADC_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("adc1_clk", IMX_SC_R_ADC_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("pwm_clk", IMX_SC_R_LCD_0_PWM_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("elcdif_pll", IMX_SC_R_ELCDIF_PLL, IMX_SC_PM_CLK_PLL); + imx_clk_scu2("lcd_clk", lcd_sels, ARRAY_SIZE(lcd_sels), IMX_SC_R_LCD_0, IMX_SC_PM_CLK_PER); + imx_clk_scu2("lcd_pxl_clk", lcd_pxl_sels, ARRAY_SIZE(lcd_pxl_sels), IMX_SC_R_LCD_0, IMX_SC_PM_CLK_MISC0); + imx_clk_scu("lcd_pxl_bypass_div_clk", IMX_SC_R_LCD_0, IMX_SC_PM_CLK_BYPASS); + + /* Audio SS */ + imx_clk_scu("audio_pll0_clk", IMX_SC_R_AUDIO_PLL_0, IMX_SC_PM_CLK_PLL); + imx_clk_scu("audio_pll1_clk", IMX_SC_R_AUDIO_PLL_1, IMX_SC_PM_CLK_PLL); + imx_clk_scu("audio_pll_div_clk0_clk", IMX_SC_R_AUDIO_PLL_0, IMX_SC_PM_CLK_MISC0); + imx_clk_scu("audio_pll_div_clk1_clk", IMX_SC_R_AUDIO_PLL_1, IMX_SC_PM_CLK_MISC0); + imx_clk_scu("audio_rec_clk0_clk", IMX_SC_R_AUDIO_PLL_0, IMX_SC_PM_CLK_MISC1); + imx_clk_scu("audio_rec_clk1_clk", IMX_SC_R_AUDIO_PLL_1, IMX_SC_PM_CLK_MISC1); + + /* Connectivity */ + imx_clk_scu("sdhc0_clk", IMX_SC_R_SDHC_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("sdhc1_clk", IMX_SC_R_SDHC_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("sdhc2_clk", IMX_SC_R_SDHC_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("enet0_root_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_PER); + imx_clk_divider_gpr_scu("enet0_ref_div", "enet0_root_clk", IMX_SC_R_ENET_0, IMX_SC_C_CLKDIV); + imx_clk_mux_gpr_scu("enet0_rgmii_txc_sel", enet0_rgmii_txc_sels, ARRAY_SIZE(enet0_rgmii_txc_sels), IMX_SC_R_ENET_0, IMX_SC_C_TXCLK); + imx_clk_scu("enet0_bypass_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_BYPASS); + imx_clk_gate_gpr_scu("enet0_ref_50_clk", "clk_dummy", IMX_SC_R_ENET_0, IMX_SC_C_DISABLE_50, true); + imx_clk_scu("enet0_rgmii_rx_clk", IMX_SC_R_ENET_0, IMX_SC_PM_CLK_MISC0); + imx_clk_scu("enet1_root_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_PER); + imx_clk_divider_gpr_scu("enet1_ref_div", "enet1_root_clk", IMX_SC_R_ENET_1, IMX_SC_C_CLKDIV); + imx_clk_mux_gpr_scu("enet1_rgmii_txc_sel", enet1_rgmii_txc_sels, ARRAY_SIZE(enet1_rgmii_txc_sels), IMX_SC_R_ENET_1, IMX_SC_C_TXCLK); + imx_clk_scu("enet1_bypass_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_BYPASS); + imx_clk_gate_gpr_scu("enet1_ref_50_clk", "clk_dummy", IMX_SC_R_ENET_1, IMX_SC_C_DISABLE_50, true); + imx_clk_scu("enet1_rgmii_rx_clk", IMX_SC_R_ENET_1, IMX_SC_PM_CLK_MISC0); + imx_clk_scu("gpmi_io_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_MST_BUS); + imx_clk_scu("gpmi_bch_clk", IMX_SC_R_NAND, IMX_SC_PM_CLK_PER); + imx_clk_scu("usb3_aclk_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_PER); + imx_clk_scu("usb3_bus_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MST_BUS); + imx_clk_scu("usb3_lpm_div", IMX_SC_R_USB_2, IMX_SC_PM_CLK_MISC); + + /* Display controller SS */ + imx_clk_scu2("dc0_disp0_clk", dc0_sels, ARRAY_SIZE(dc0_sels), IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC0); + imx_clk_scu2("dc0_disp1_clk", dc0_sels, ARRAY_SIZE(dc0_sels), IMX_SC_R_DC_0, IMX_SC_PM_CLK_MISC1); + imx_clk_scu("dc0_pll0_clk", IMX_SC_R_DC_0_PLL_0, IMX_SC_PM_CLK_PLL); + imx_clk_scu("dc0_pll1_clk", IMX_SC_R_DC_0_PLL_1, IMX_SC_PM_CLK_PLL); + imx_clk_scu("dc0_bypass0_clk", IMX_SC_R_DC_0_VIDEO0, IMX_SC_PM_CLK_BYPASS); + imx_clk_scu("dc0_bypass1_clk", IMX_SC_R_DC_0_VIDEO1, IMX_SC_PM_CLK_BYPASS); + + imx_clk_scu2("dc1_disp0_clk", dc1_sels, ARRAY_SIZE(dc1_sels), IMX_SC_R_DC_1, IMX_SC_PM_CLK_MISC0); + imx_clk_scu2("dc1_disp1_clk", dc1_sels, ARRAY_SIZE(dc1_sels), IMX_SC_R_DC_1, IMX_SC_PM_CLK_MISC1); + imx_clk_scu("dc1_pll0_clk", IMX_SC_R_DC_1_PLL_0, IMX_SC_PM_CLK_PLL); + imx_clk_scu("dc1_pll1_clk", IMX_SC_R_DC_1_PLL_1, IMX_SC_PM_CLK_PLL); + imx_clk_scu("dc1_bypass0_clk", IMX_SC_R_DC_1_VIDEO0, IMX_SC_PM_CLK_BYPASS); + imx_clk_scu("dc1_bypass1_clk", IMX_SC_R_DC_1_VIDEO1, IMX_SC_PM_CLK_BYPASS); + + /* MIPI-LVDS SS */ + imx_clk_scu("mipi0_bypass_clk", IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_BYPASS); + imx_clk_scu("mipi0_pixel_clk", IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("mipi0_lvds_pixel_clk", IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("mipi0_lvds_bypass_clk", IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_BYPASS); + imx_clk_scu("mipi0_lvds_phy_clk", IMX_SC_R_LVDS_0, IMX_SC_PM_CLK_MISC3); + imx_clk_scu2("mipi0_dsi_tx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_MST_BUS); + imx_clk_scu2("mipi0_dsi_rx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_SLV_BUS); + imx_clk_scu2("mipi0_dsi_phy_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_0, IMX_SC_PM_CLK_PHY); + imx_clk_scu("mipi0_i2c0_clk", IMX_SC_R_MIPI_0_I2C_0, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("mipi0_i2c1_clk", IMX_SC_R_MIPI_0_I2C_1, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("mipi0_pwm0_clk", IMX_SC_R_MIPI_0_PWM_0, IMX_SC_PM_CLK_PER); + + imx_clk_scu("mipi1_bypass_clk", IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_BYPASS); + imx_clk_scu("mipi1_pixel_clk", IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("mipi1_lvds_pixel_clk", IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("mipi1_lvds_bypass_clk", IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_BYPASS); + imx_clk_scu("mipi1_lvds_phy_clk", IMX_SC_R_LVDS_1, IMX_SC_PM_CLK_MISC3); + + imx_clk_scu2("mipi1_dsi_tx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_MST_BUS); + imx_clk_scu2("mipi1_dsi_rx_esc_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_SLV_BUS); + imx_clk_scu2("mipi1_dsi_phy_clk", mipi_sels, ARRAY_SIZE(mipi_sels), IMX_SC_R_MIPI_1, IMX_SC_PM_CLK_PHY); + imx_clk_scu("mipi1_i2c0_clk", IMX_SC_R_MIPI_1_I2C_0, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("mipi1_i2c1_clk", IMX_SC_R_MIPI_1_I2C_1, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("mipi1_pwm0_clk", IMX_SC_R_MIPI_1_PWM_0, IMX_SC_PM_CLK_PER); + + imx_clk_scu("lvds0_i2c0_clk", IMX_SC_R_LVDS_0_I2C_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("lvds0_i2c1_clk", IMX_SC_R_LVDS_0_I2C_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("lvds0_pwm0_clk", IMX_SC_R_LVDS_0_PWM_0, IMX_SC_PM_CLK_PER); + + imx_clk_scu("lvds1_i2c0_clk", IMX_SC_R_LVDS_1_I2C_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("lvds1_i2c1_clk", IMX_SC_R_LVDS_1_I2C_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("lvds1_pwm0_clk", IMX_SC_R_LVDS_1_PWM_0, IMX_SC_PM_CLK_PER); + + /* MIPI CSI SS */ + imx_clk_scu("mipi_csi0_core_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("mipi_csi0_esc_clk", IMX_SC_R_CSI_0, IMX_SC_PM_CLK_MISC); + imx_clk_scu("mipi_csi0_i2c0_clk", IMX_SC_R_CSI_0_I2C_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("mipi_csi0_pwm0_clk", IMX_SC_R_CSI_0_PWM_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("mipi_csi1_core_clk", IMX_SC_R_CSI_1, IMX_SC_PM_CLK_PER); + imx_clk_scu("mipi_csi1_esc_clk", IMX_SC_R_CSI_1, IMX_SC_PM_CLK_MISC); + imx_clk_scu("mipi_csi1_i2c0_clk", IMX_SC_R_CSI_1_I2C_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("mipi_csi1_pwm0_clk", IMX_SC_R_CSI_1_PWM_0, IMX_SC_PM_CLK_PER); + + /* Parallel Interface SS */ + imx_clk_scu("pi_dpll_clk", IMX_SC_R_PI_0_PLL, IMX_SC_PM_CLK_PLL); + imx_clk_scu2("pi_per_div_clk", pi_pll0_sels, ARRAY_SIZE(pi_pll0_sels), IMX_SC_R_PI_0, IMX_SC_PM_CLK_PER); + imx_clk_scu("pi_mclk_div_clk", IMX_SC_R_PI_0, IMX_SC_PM_CLK_MISC0); + imx_clk_scu("pi_i2c0_div_clk", IMX_SC_R_PI_0_I2C_0, IMX_SC_PM_CLK_PER); + + /* GPU SS */ + imx_clk_scu("gpu_core0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_PER); + imx_clk_scu("gpu_shader0_clk", IMX_SC_R_GPU_0_PID0, IMX_SC_PM_CLK_MISC); + + imx_clk_scu("gpu_core1_clk", IMX_SC_R_GPU_1_PID0, IMX_SC_PM_CLK_PER); + imx_clk_scu("gpu_shader1_clk", IMX_SC_R_GPU_1_PID0, IMX_SC_PM_CLK_MISC); + + /* CM40 SS */ + imx_clk_scu("cm40_i2c_div", IMX_SC_R_M4_0_I2C, IMX_SC_PM_CLK_PER); + imx_clk_scu("cm40_lpuart_div", IMX_SC_R_M4_0_UART, IMX_SC_PM_CLK_PER); + + /* CM41 SS */ + imx_clk_scu("cm41_i2c_div", IMX_SC_R_M4_1_I2C, IMX_SC_PM_CLK_PER); + + /* HDMI TX SS */ + imx_clk_scu("hdmi_dig_pll_clk", IMX_SC_R_HDMI_PLL_0, IMX_SC_PM_CLK_PLL); + imx_clk_scu("hdmi_av_pll_clk", IMX_SC_R_HDMI_PLL_1, IMX_SC_PM_CLK_PLL); + imx_clk_scu2("hdmi_pixel_mux_clk", hdmi_sels, ARRAY_SIZE(hdmi_sels), IMX_SC_R_HDMI, IMX_SC_PM_CLK_MISC0); + imx_clk_scu2("hdmi_pixel_link_clk", hdmi_sels, ARRAY_SIZE(hdmi_sels), IMX_SC_R_HDMI, IMX_SC_PM_CLK_MISC1); + imx_clk_scu("hdmi_ipg_clk", IMX_SC_R_HDMI, IMX_SC_PM_CLK_MISC4); + imx_clk_scu("hdmi_i2c0_clk", IMX_SC_R_HDMI_I2C_0, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("hdmi_hdp_core_clk", IMX_SC_R_HDMI, IMX_SC_PM_CLK_MISC2); + imx_clk_scu2("hdmi_pxl_clk", hdmi_sels, ARRAY_SIZE(hdmi_sels), IMX_SC_R_HDMI, IMX_SC_PM_CLK_MISC3); + imx_clk_scu("hdmi_i2s_bypass_clk", IMX_SC_R_HDMI_I2S, IMX_SC_PM_CLK_BYPASS); + imx_clk_scu("hdmi_i2s_clk", IMX_SC_R_HDMI_I2S, IMX_SC_PM_CLK_MISC0); + + /* HDMI RX SS */ + imx_clk_scu("hdmi_rx_i2s_bypass_clk", IMX_SC_R_HDMI_RX_BYPASS, IMX_SC_PM_CLK_MISC0); + imx_clk_scu("hdmi_rx_spdif_bypass_clk", IMX_SC_R_HDMI_RX_BYPASS, IMX_SC_PM_CLK_MISC1); + imx_clk_scu("hdmi_rx_bypass_clk", IMX_SC_R_HDMI_RX_BYPASS, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("hdmi_rx_i2c0_clk", IMX_SC_R_HDMI_RX_I2C_0, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("hdmi_rx_pwm_clk", IMX_SC_R_HDMI_RX_PWM_0, IMX_SC_PM_CLK_MISC2); + imx_clk_scu("hdmi_rx_spdif_clk", IMX_SC_R_HDMI_RX, IMX_SC_PM_CLK_MISC0); + imx_clk_scu2("hdmi_rx_hd_ref_clk", hdmi_rx_sels, ARRAY_SIZE(hdmi_rx_sels), IMX_SC_R_HDMI_RX, IMX_SC_PM_CLK_MISC1); + imx_clk_scu2("hdmi_rx_hd_core_clk", hdmi_rx_sels, ARRAY_SIZE(hdmi_rx_sels), IMX_SC_R_HDMI_RX, IMX_SC_PM_CLK_MISC2); + imx_clk_scu2("hdmi_rx_pxl_clk", hdmi_rx_sels, ARRAY_SIZE(hdmi_rx_sels), IMX_SC_R_HDMI_RX, IMX_SC_PM_CLK_MISC3); + imx_clk_scu("hdmi_rx_i2s_clk", IMX_SC_R_HDMI_RX, IMX_SC_PM_CLK_MISC4); + + ret = of_clk_add_hw_provider(ccm_node, imx_scu_of_clk_src_get, imx_scu_clks); + if (ret) + imx_clk_scu_unregister(); + + return ret; +} + +static const struct of_device_id imx8qxp_match[] = { + { .compatible = "fsl,scu-clk", }, + { .compatible = "fsl,imx8dxl-clk", &imx_clk_scu_rsrc_imx8dxl, }, + { .compatible = "fsl,imx8qxp-clk", &imx_clk_scu_rsrc_imx8qxp, }, + { .compatible = "fsl,imx8qm-clk", &imx_clk_scu_rsrc_imx8qm, }, + { /* sentinel */ } +}; + +static struct platform_driver imx8qxp_clk_driver = { + .driver = { + .name = "imx8qxp-clk", + .of_match_table = imx8qxp_match, + .suppress_bind_attrs = true, + }, + .probe = imx8qxp_clk_probe, +}; +module_platform_driver(imx8qxp_clk_driver); + +MODULE_AUTHOR("Aisheng Dong <aisheng.dong@nxp.com>"); +MODULE_DESCRIPTION("NXP i.MX8QXP clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imx8ulp.c b/drivers/clk/imx/clk-imx8ulp.c new file mode 100644 index 000000000..fa9121b3c --- /dev/null +++ b/drivers/clk/imx/clk-imx8ulp.c @@ -0,0 +1,570 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include <dt-bindings/clock/imx8ulp-clock.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> + +#include "clk.h" + +static const char * const pll_pre_sels[] = { "sosc", "frosc", }; +static const char * const a35_sels[] = { "frosc", "spll2", "sosc", "lvds", }; +static const char * const nic_sels[] = { "frosc", "spll3_pfd0", "sosc", "lvds", }; +static const char * const pcc3_periph_bus_sels[] = { "dummy", "lposc", "sosc_div2", + "frosc_div2", "xbar_divbus", "spll3_pfd1_div1", + "spll3_pfd0_div2", "spll3_pfd0_div1", }; +static const char * const pcc4_periph_bus_sels[] = { "dummy", "dummy", "lposc", + "sosc_div2", "frosc_div2", "xbar_divbus", + "spll3_vcodiv", "spll3_pfd0_div1", }; +static const char * const pcc4_periph_plat_sels[] = { "dummy", "sosc_div1", "frosc_div1", + "spll3_pfd3_div2", "spll3_pfd3_div1", + "spll3_pfd2_div2", "spll3_pfd2_div1", + "spll3_pfd1_div2", }; +static const char * const pcc5_periph_bus_sels[] = { "dummy", "dummy", "lposc", + "sosc_div2", "frosc_div2", "lpav_bus_clk", + "pll4_vcodiv", "pll4_pfd3_div1", }; +static const char * const pcc5_periph_plat_sels[] = { "dummy", "pll4_pfd3_div2", "pll4_pfd2_div2", + "pll4_pfd2_div1", "pll4_pfd1_div2", + "pll4_pfd1_div1", "pll4_pfd0_div2", + "pll4_pfd0_div1", }; +static const char * const hifi_sels[] = { "frosc", "pll4", "pll4_pfd0", "sosc", + "lvds", "dummy", "dummy", "dummy", }; +static const char * const ddr_sels[] = { "frosc", "pll4_pfd1", "sosc", "lvds", + "pll4", "pll4", "pll4", "pll4", }; +static const char * const lpav_sels[] = { "frosc", "pll4_pfd1", "sosc", "lvds", }; +static const char * const sai45_sels[] = { "spll3_pfd1_div1", "aud_clk1", "aud_clk2", "sosc", }; +static const char * const sai67_sels[] = { "spll1_pfd2_div", "spll3_pfd1_div1", "aud_clk0", "aud_clk1", "aud_clk2", "sosc", "dummy", "dummy", }; +static const char * const aud_clk1_sels[] = { "ext_aud_mclk2", "sai4_rx_bclk", "sai4_tx_bclk", "sai5_rx_bclk", "sai5_tx_bclk", "dummy", "dummy", "dummy", }; +static const char * const aud_clk2_sels[] = { "ext_aud_mclk3", "sai6_rx_bclk", "sai6_tx_bclk", "sai7_rx_bclk", "sai7_tx_bclk", "spdif_rx", "dummy", "dummy", }; +static const char * const enet_ts_sels[] = { "ext_rmii_clk", "ext_ts_clk", "rosc", "ext_aud_mclk", "sosc", "dummy", "dummy", "dummy"}; +static const char * const xbar_divbus[] = { "xbar_divbus" }; +static const char * const nic_per_divplat[] = { "nic_per_divplat" }; +static const char * const lpav_axi_div[] = { "lpav_axi_div" }; +static const char * const lpav_bus_div[] = { "lpav_bus_div" }; + +struct pcc_reset_dev { + void __iomem *base; + struct reset_controller_dev rcdev; + const u32 *resets; + /* Set to imx_ccm_lock to protect register access shared with clock control */ + spinlock_t *lock; +}; + +#define PCC_SW_RST BIT(28) +#define to_pcc_reset_dev(_rcdev) container_of(_rcdev, struct pcc_reset_dev, rcdev) + +static const u32 pcc3_resets[] = { + 0xa8, 0xac, 0xc8, 0xcc, 0xd0, + 0xd4, 0xd8, 0xdc, 0xe0, 0xe4, + 0xe8, 0xec, 0xf0 +}; + +static const u32 pcc4_resets[] = { + 0x4, 0x8, 0xc, 0x10, 0x14, + 0x18, 0x1c, 0x20, 0x24, 0x34, + 0x38, 0x3c, 0x40, 0x44, 0x48, + 0x4c, 0x54 +}; + +static const u32 pcc5_resets[] = { + 0xa0, 0xa4, 0xa8, 0xac, 0xb0, + 0xb4, 0xbc, 0xc0, 0xc8, 0xcc, + 0xd0, 0xf0, 0xf4, 0xf8 +}; + +static int imx8ulp_pcc_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev); + u32 offset = pcc_reset->resets[id]; + unsigned long flags; + u32 val; + + spin_lock_irqsave(pcc_reset->lock, flags); + + val = readl(pcc_reset->base + offset); + val &= ~PCC_SW_RST; + writel(val, pcc_reset->base + offset); + + spin_unlock_irqrestore(pcc_reset->lock, flags); + + return 0; +} + +static int imx8ulp_pcc_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct pcc_reset_dev *pcc_reset = to_pcc_reset_dev(rcdev); + u32 offset = pcc_reset->resets[id]; + unsigned long flags; + u32 val; + + spin_lock_irqsave(pcc_reset->lock, flags); + + val = readl(pcc_reset->base + offset); + val |= PCC_SW_RST; + writel(val, pcc_reset->base + offset); + + spin_unlock_irqrestore(pcc_reset->lock, flags); + + return 0; +} + +static const struct reset_control_ops imx8ulp_pcc_reset_ops = { + .assert = imx8ulp_pcc_assert, + .deassert = imx8ulp_pcc_deassert, +}; + +static int imx8ulp_pcc_reset_init(struct platform_device *pdev, void __iomem *base, + const u32 *resets, unsigned int nr_resets) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct pcc_reset_dev *pcc_reset; + + pcc_reset = devm_kzalloc(dev, sizeof(*pcc_reset), GFP_KERNEL); + if (!pcc_reset) + return -ENOMEM; + + pcc_reset->base = base; + pcc_reset->lock = &imx_ccm_lock; + pcc_reset->resets = resets; + pcc_reset->rcdev.owner = THIS_MODULE; + pcc_reset->rcdev.nr_resets = nr_resets; + pcc_reset->rcdev.ops = &imx8ulp_pcc_reset_ops; + pcc_reset->rcdev.of_node = np; + + return devm_reset_controller_register(dev, &pcc_reset->rcdev); +} + +static int imx8ulp_clk_cgc1_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_CGC1_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_CGC1_END; + clks = clk_data->hws; + + clks[IMX8ULP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + /* CGC1 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_SPLL2_PRE_SEL] = imx_clk_hw_mux_flags("spll2_pre_sel", base + 0x510, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + clks[IMX8ULP_CLK_SPLL3_PRE_SEL] = imx_clk_hw_mux_flags("spll3_pre_sel", base + 0x610, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + + clks[IMX8ULP_CLK_SPLL2] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP_1GHZ, "spll2", "spll2_pre_sel", base + 0x500); + clks[IMX8ULP_CLK_SPLL3] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP, "spll3", "spll3_pre_sel", base + 0x600); + clks[IMX8ULP_CLK_SPLL3_VCODIV] = imx_clk_hw_divider("spll3_vcodiv", "spll3", base + 0x604, 0, 6); + + clks[IMX8ULP_CLK_SPLL3_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd0", "spll3_vcodiv", base + 0x614, 0); + clks[IMX8ULP_CLK_SPLL3_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd1", "spll3_vcodiv", base + 0x614, 1); + clks[IMX8ULP_CLK_SPLL3_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd2", "spll3_vcodiv", base + 0x614, 2); + clks[IMX8ULP_CLK_SPLL3_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "spll3_pfd3", "spll3_vcodiv", base + 0x614, 3); + + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd0_div1_gate", "spll3_pfd0", base + 0x608, 7); + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd0_div2_gate", "spll3_pfd0", base + 0x608, 15); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd1_div1_gate", "spll3_pfd1", base + 0x608, 23); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd1_div2_gate", "spll3_pfd1", base + 0x608, 31); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd2_div1_gate", "spll3_pfd2", base + 0x60c, 7); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd2_div2_gate", "spll3_pfd2", base + 0x60c, 15); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV1_GATE] = imx_clk_hw_gate_dis("spll3_pfd3_div1_gate", "spll3_pfd3", base + 0x60c, 23); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV2_GATE] = imx_clk_hw_gate_dis("spll3_pfd3_div2_gate", "spll3_pfd3", base + 0x60c, 31); + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV1] = imx_clk_hw_divider("spll3_pfd0_div1", "spll3_pfd0_div1_gate", base + 0x608, 0, 6); + clks[IMX8ULP_CLK_SPLL3_PFD0_DIV2] = imx_clk_hw_divider("spll3_pfd0_div2", "spll3_pfd0_div2_gate", base + 0x608, 8, 6); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV1] = imx_clk_hw_divider("spll3_pfd1_div1", "spll3_pfd1_div1_gate", base + 0x608, 16, 6); + clks[IMX8ULP_CLK_SPLL3_PFD1_DIV2] = imx_clk_hw_divider("spll3_pfd1_div2", "spll3_pfd1_div2_gate", base + 0x608, 24, 6); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV1] = imx_clk_hw_divider("spll3_pfd2_div1", "spll3_pfd2_div1_gate", base + 0x60c, 0, 6); + clks[IMX8ULP_CLK_SPLL3_PFD2_DIV2] = imx_clk_hw_divider("spll3_pfd2_div2", "spll3_pfd2_div2_gate", base + 0x60c, 8, 6); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV1] = imx_clk_hw_divider("spll3_pfd3_div1", "spll3_pfd3_div1_gate", base + 0x60c, 16, 6); + clks[IMX8ULP_CLK_SPLL3_PFD3_DIV2] = imx_clk_hw_divider("spll3_pfd3_div2", "spll3_pfd3_div2_gate", base + 0x60c, 24, 6); + + clks[IMX8ULP_CLK_A35_SEL] = imx_clk_hw_mux2("a35_sel", base + 0x14, 28, 2, a35_sels, ARRAY_SIZE(a35_sels)); + clks[IMX8ULP_CLK_A35_DIV] = imx_clk_hw_divider_flags("a35_div", "a35_sel", base + 0x14, 21, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + + clks[IMX8ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x34, 28, 2, nic_sels, ARRAY_SIZE(nic_sels)); + clks[IMX8ULP_CLK_NIC_AD_DIVPLAT] = imx_clk_hw_divider_flags("nic_ad_divplat", "nic_sel", base + 0x34, 21, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_NIC_PER_DIVPLAT] = imx_clk_hw_divider_flags("nic_per_divplat", "nic_ad_divplat", base + 0x34, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_AD_DIVPLAT] = imx_clk_hw_divider_flags("xbar_ad_divplat", "nic_ad_divplat", base + 0x38, 14, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_DIVBUS] = imx_clk_hw_divider_flags("xbar_divbus", "xbar_ad_divplat", base + 0x38, 7, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_XBAR_AD_SLOW] = imx_clk_hw_divider_flags("xbar_ad_slow", "xbar_divbus", base + 0x38, 0, 6, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + + clks[IMX8ULP_CLK_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("sosc_div1_gate", "sosc", base + 0x108, 7); + clks[IMX8ULP_CLK_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("sosc_div2_gate", "sosc", base + 0x108, 15); + clks[IMX8ULP_CLK_SOSC_DIV3_GATE] = imx_clk_hw_gate_dis("sosc_div3_gate", "sosc", base + 0x108, 23); + clks[IMX8ULP_CLK_SOSC_DIV1] = imx_clk_hw_divider("sosc_div1", "sosc_div1_gate", base + 0x108, 0, 6); + clks[IMX8ULP_CLK_SOSC_DIV2] = imx_clk_hw_divider("sosc_div2", "sosc_div2_gate", base + 0x108, 8, 6); + clks[IMX8ULP_CLK_SOSC_DIV3] = imx_clk_hw_divider("sosc_div3", "sosc_div3_gate", base + 0x108, 16, 6); + + clks[IMX8ULP_CLK_FROSC_DIV1_GATE] = imx_clk_hw_gate_dis("frosc_div1_gate", "frosc", base + 0x208, 7); + clks[IMX8ULP_CLK_FROSC_DIV2_GATE] = imx_clk_hw_gate_dis("frosc_div2_gate", "frosc", base + 0x208, 15); + clks[IMX8ULP_CLK_FROSC_DIV3_GATE] = imx_clk_hw_gate_dis("frosc_div3_gate", "frosc", base + 0x208, 23); + clks[IMX8ULP_CLK_FROSC_DIV1] = imx_clk_hw_divider("frosc_div1", "frosc_div1_gate", base + 0x208, 0, 6); + clks[IMX8ULP_CLK_FROSC_DIV2] = imx_clk_hw_divider("frosc_div2", "frosc_div2_gate", base + 0x208, 8, 6); + clks[IMX8ULP_CLK_FROSC_DIV3] = imx_clk_hw_divider("frosc_div3", "frosc_div3_gate", base + 0x208, 16, 6); + clks[IMX8ULP_CLK_AUD_CLK1] = imx_clk_hw_mux2("aud_clk1", base + 0x900, 0, 3, aud_clk1_sels, ARRAY_SIZE(aud_clk1_sels)); + clks[IMX8ULP_CLK_SAI4_SEL] = imx_clk_hw_mux2("sai4_sel", base + 0x904, 0, 2, sai45_sels, ARRAY_SIZE(sai45_sels)); + clks[IMX8ULP_CLK_SAI5_SEL] = imx_clk_hw_mux2("sai5_sel", base + 0x904, 8, 2, sai45_sels, ARRAY_SIZE(sai45_sels)); + clks[IMX8ULP_CLK_ENET_TS_SEL] = imx_clk_hw_mux2("enet_ts", base + 0x700, 24, 3, enet_ts_sels, ARRAY_SIZE(enet_ts_sels)); + + imx_check_clk_hws(clks, clk_data->num); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +static int imx8ulp_clk_cgc2_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_CGC2_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_CGC2_END; + clks = clk_data->hws; + + /* CGC2 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_PLL4_PRE_SEL] = imx_clk_hw_mux_flags("pll4_pre_sel", base + 0x610, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + + clks[IMX8ULP_CLK_PLL4] = imx_clk_hw_pllv4(IMX_PLLV4_IMX8ULP, "pll4", "pll4_pre_sel", base + 0x600); + clks[IMX8ULP_CLK_PLL4_VCODIV] = imx_clk_hw_divider("pll4_vcodiv", "pll4", base + 0x604, 0, 6); + + clks[IMX8ULP_CLK_HIFI_SEL] = imx_clk_hw_mux_flags("hifi_sel", base + 0x14, 28, 3, hifi_sels, ARRAY_SIZE(hifi_sels), CLK_SET_PARENT_GATE); + clks[IMX8ULP_CLK_HIFI_DIVCORE] = imx_clk_hw_divider("hifi_core_div", "hifi_sel", base + 0x14, 21, 6); + clks[IMX8ULP_CLK_HIFI_DIVPLAT] = imx_clk_hw_divider("hifi_plat_div", "hifi_core_div", base + 0x14, 14, 6); + + clks[IMX8ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x40, 28, 3, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_PARENT_GATE); + clks[IMX8ULP_CLK_DDR_DIV] = imx_clk_hw_divider_flags("ddr_div", "ddr_sel", base + 0x40, 21, 6, CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_LPAV_AXI_SEL] = imx_clk_hw_mux("lpav_sel", base + 0x3c, 28, 2, lpav_sels, ARRAY_SIZE(lpav_sels)); + clks[IMX8ULP_CLK_LPAV_AXI_DIV] = imx_clk_hw_divider_flags("lpav_axi_div", "lpav_sel", base + 0x3c, 21, 6, CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_LPAV_AHB_DIV] = imx_clk_hw_divider_flags("lpav_ahb_div", "lpav_axi_div", base + 0x3c, 14, 6, CLK_IS_CRITICAL); + clks[IMX8ULP_CLK_LPAV_BUS_DIV] = imx_clk_hw_divider_flags("lpav_bus_div", "lpav_axi_div", base + 0x3c, 7, 6, CLK_IS_CRITICAL); + + clks[IMX8ULP_CLK_PLL4_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd0", "pll4_vcodiv", base + 0x614, 0); + clks[IMX8ULP_CLK_PLL4_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd1", "pll4_vcodiv", base + 0x614, 1); + clks[IMX8ULP_CLK_PLL4_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd2", "pll4_vcodiv", base + 0x614, 2); + clks[IMX8ULP_CLK_PLL4_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX8ULP, "pll4_pfd3", "pll4_vcodiv", base + 0x614, 3); + + clks[IMX8ULP_CLK_PLL4_PFD0_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd0_div1_gate", "pll4_pfd0", base + 0x608, 7); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd0_div2_gate", "pll4_pfd0", base + 0x608, 15); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd1_div1_gate", "pll4_pfd1", base + 0x608, 23); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd1_div2_gate", "pll4_pfd1", base + 0x608, 31); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd2_div1_gate", "pll4_pfd2", base + 0x60c, 7); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd2_div2_gate", "pll4_pfd2", base + 0x60c, 15); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV1_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div1_gate", "pll4_pfd3", base + 0x60c, 23); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV2_GATE] = imx_clk_hw_gate_dis("pll4_pfd3_div2_gate", "pll4_pfd3", base + 0x60c, 31); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV1] = imx_clk_hw_divider("pll4_pfd0_div1", "pll4_pfd0_div1_gate", base + 0x608, 0, 6); + clks[IMX8ULP_CLK_PLL4_PFD0_DIV2] = imx_clk_hw_divider("pll4_pfd0_div2", "pll4_pfd0_div2_gate", base + 0x608, 8, 6); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV1] = imx_clk_hw_divider("pll4_pfd1_div1", "pll4_pfd1_div1_gate", base + 0x608, 16, 6); + clks[IMX8ULP_CLK_PLL4_PFD1_DIV2] = imx_clk_hw_divider("pll4_pfd1_div2", "pll4_pfd1_div2_gate", base + 0x608, 24, 6); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV1] = imx_clk_hw_divider("pll4_pfd2_div1", "pll4_pfd2_div1_gate", base + 0x60c, 0, 6); + clks[IMX8ULP_CLK_PLL4_PFD2_DIV2] = imx_clk_hw_divider("pll4_pfd2_div2", "pll4_pfd2_div2_gate", base + 0x60c, 8, 6); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV1] = imx_clk_hw_divider("pll4_pfd3_div1", "pll4_pfd3_div1_gate", base + 0x60c, 16, 6); + clks[IMX8ULP_CLK_PLL4_PFD3_DIV2] = imx_clk_hw_divider("pll4_pfd3_div2", "pll4_pfd3_div2_gate", base + 0x60c, 24, 6); + + clks[IMX8ULP_CLK_CGC2_SOSC_DIV1_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div1_gate", "sosc", base + 0x108, 7); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV2_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div2_gate", "sosc", base + 0x108, 15); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV3_GATE] = imx_clk_hw_gate_dis("cgc2_sosc_div3_gate", "sosc", base + 0x108, 23); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV1] = imx_clk_hw_divider("cgc2_sosc_div1", "cgc2_sosc_div1_gate", base + 0x108, 0, 6); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV2] = imx_clk_hw_divider("cgc2_sosc_div2", "cgc2_sosc_div2_gate", base + 0x108, 8, 6); + clks[IMX8ULP_CLK_CGC2_SOSC_DIV3] = imx_clk_hw_divider("cgc2_sosc_div3", "cgc2_sosc_div3_gate", base + 0x108, 16, 6); + + clks[IMX8ULP_CLK_CGC2_FROSC_DIV1_GATE] = imx_clk_hw_gate_dis("cgc2_frosc_div1_gate", "frosc", base + 0x208, 7); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV2_GATE] = imx_clk_hw_gate_dis("cgc2_frosc_div2_gate", "frosc", base + 0x208, 15); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV3_GATE] = imx_clk_hw_gate_dis("cgc2_frosc_div3_gate", "frosc", base + 0x208, 23); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV1] = imx_clk_hw_divider("cgc2_frosc_div1", "cgc2_frosc_div1_gate", base + 0x208, 0, 6); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV2] = imx_clk_hw_divider("cgc2_frosc_div2", "cgc2_frosc_div2_gate", base + 0x208, 8, 6); + clks[IMX8ULP_CLK_CGC2_FROSC_DIV3] = imx_clk_hw_divider("cgc2_frosc_div3", "cgc2_frosc_div3_gate", base + 0x208, 16, 6); + clks[IMX8ULP_CLK_AUD_CLK2] = imx_clk_hw_mux2("aud_clk2", base + 0x900, 0, 3, aud_clk2_sels, ARRAY_SIZE(aud_clk2_sels)); + clks[IMX8ULP_CLK_SAI6_SEL] = imx_clk_hw_mux2("sai6_sel", base + 0x904, 0, 3, sai67_sels, ARRAY_SIZE(sai67_sels)); + clks[IMX8ULP_CLK_SAI7_SEL] = imx_clk_hw_mux2("sai7_sel", base + 0x904, 8, 3, sai67_sels, ARRAY_SIZE(sai67_sels)); + clks[IMX8ULP_CLK_SPDIF_SEL] = imx_clk_hw_mux2("spdif_sel", base + 0x910, 0, 3, sai67_sels, ARRAY_SIZE(sai67_sels)); + clks[IMX8ULP_CLK_DSI_PHY_REF] = imx_clk_hw_fixed("dsi_phy_ref", 24000000); + + imx_check_clk_hws(clks, clk_data->num); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} + +static int imx8ulp_clk_pcc3_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + int ret; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC3_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_PCC3_END; + clks = clk_data->hws; + + /* PCC3 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_WDOG3] = imx8ulp_clk_hw_composite("wdog3", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xa8, 1); + clks[IMX8ULP_CLK_WDOG4] = imx8ulp_clk_hw_composite("wdog4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xac, 1); + clks[IMX8ULP_CLK_LPIT1] = imx8ulp_clk_hw_composite("lpit1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xc8, 1); + clks[IMX8ULP_CLK_TPM4] = imx8ulp_clk_hw_composite("tpm4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xcc, 1); + clks[IMX8ULP_CLK_TPM5] = imx8ulp_clk_hw_composite("tpm5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd0, 1); + clks[IMX8ULP_CLK_FLEXIO1] = imx8ulp_clk_hw_composite("flexio1", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd4, 1); + clks[IMX8ULP_CLK_I3C2] = imx8ulp_clk_hw_composite("i3c2", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xd8, 1); + clks[IMX8ULP_CLK_LPI2C4] = imx8ulp_clk_hw_composite("lpi2c4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xdc, 1); + clks[IMX8ULP_CLK_LPI2C5] = imx8ulp_clk_hw_composite("lpi2c5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xe0, 1); + clks[IMX8ULP_CLK_LPUART4] = imx8ulp_clk_hw_composite("lpuart4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xe4, 1); + clks[IMX8ULP_CLK_LPUART5] = imx8ulp_clk_hw_composite("lpuart5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xe8, 1); + clks[IMX8ULP_CLK_LPSPI4] = imx8ulp_clk_hw_composite("lpspi4", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xec, 1); + clks[IMX8ULP_CLK_LPSPI5] = imx8ulp_clk_hw_composite("lpspi5", pcc3_periph_bus_sels, ARRAY_SIZE(pcc3_periph_bus_sels), true, true, true, base + 0xf0, 1); + + clks[IMX8ULP_CLK_DMA1_MP] = imx_clk_hw_gate("pcc_dma1_mp", "xbar_ad_divplat", base + 0x4, 30); + clks[IMX8ULP_CLK_DMA1_CH0] = imx_clk_hw_gate("pcc_dma1_ch0", "xbar_ad_divplat", base + 0x8, 30); + clks[IMX8ULP_CLK_DMA1_CH1] = imx_clk_hw_gate("pcc_dma1_ch1", "xbar_ad_divplat", base + 0xc, 30); + clks[IMX8ULP_CLK_DMA1_CH2] = imx_clk_hw_gate("pcc_dma1_ch2", "xbar_ad_divplat", base + 0x10, 30); + clks[IMX8ULP_CLK_DMA1_CH3] = imx_clk_hw_gate("pcc_dma1_ch3", "xbar_ad_divplat", base + 0x14, 30); + clks[IMX8ULP_CLK_DMA1_CH4] = imx_clk_hw_gate("pcc_dma1_ch4", "xbar_ad_divplat", base + 0x18, 30); + clks[IMX8ULP_CLK_DMA1_CH5] = imx_clk_hw_gate("pcc_dma1_ch5", "xbar_ad_divplat", base + 0x1c, 30); + clks[IMX8ULP_CLK_DMA1_CH6] = imx_clk_hw_gate("pcc_dma1_ch6", "xbar_ad_divplat", base + 0x20, 30); + clks[IMX8ULP_CLK_DMA1_CH7] = imx_clk_hw_gate("pcc_dma1_ch7", "xbar_ad_divplat", base + 0x24, 30); + clks[IMX8ULP_CLK_DMA1_CH8] = imx_clk_hw_gate("pcc_dma1_ch8", "xbar_ad_divplat", base + 0x28, 30); + clks[IMX8ULP_CLK_DMA1_CH9] = imx_clk_hw_gate("pcc_dma1_ch9", "xbar_ad_divplat", base + 0x2c, 30); + clks[IMX8ULP_CLK_DMA1_CH10] = imx_clk_hw_gate("pcc_dma1_ch10", "xbar_ad_divplat", base + 0x30, 30); + clks[IMX8ULP_CLK_DMA1_CH11] = imx_clk_hw_gate("pcc_dma1_ch11", "xbar_ad_divplat", base + 0x34, 30); + clks[IMX8ULP_CLK_DMA1_CH12] = imx_clk_hw_gate("pcc_dma1_ch12", "xbar_ad_divplat", base + 0x38, 30); + clks[IMX8ULP_CLK_DMA1_CH13] = imx_clk_hw_gate("pcc_dma1_ch13", "xbar_ad_divplat", base + 0x3c, 30); + clks[IMX8ULP_CLK_DMA1_CH14] = imx_clk_hw_gate("pcc_dma1_ch14", "xbar_ad_divplat", base + 0x40, 30); + clks[IMX8ULP_CLK_DMA1_CH15] = imx_clk_hw_gate("pcc_dma1_ch15", "xbar_ad_divplat", base + 0x44, 30); + clks[IMX8ULP_CLK_DMA1_CH16] = imx_clk_hw_gate("pcc_dma1_ch16", "xbar_ad_divplat", base + 0x48, 30); + clks[IMX8ULP_CLK_DMA1_CH17] = imx_clk_hw_gate("pcc_dma1_ch17", "xbar_ad_divplat", base + 0x4c, 30); + clks[IMX8ULP_CLK_DMA1_CH18] = imx_clk_hw_gate("pcc_dma1_ch18", "xbar_ad_divplat", base + 0x50, 30); + clks[IMX8ULP_CLK_DMA1_CH19] = imx_clk_hw_gate("pcc_dma1_ch19", "xbar_ad_divplat", base + 0x54, 30); + clks[IMX8ULP_CLK_DMA1_CH20] = imx_clk_hw_gate("pcc_dma1_ch20", "xbar_ad_divplat", base + 0x58, 30); + clks[IMX8ULP_CLK_DMA1_CH21] = imx_clk_hw_gate("pcc_dma1_ch21", "xbar_ad_divplat", base + 0x5c, 30); + clks[IMX8ULP_CLK_DMA1_CH22] = imx_clk_hw_gate("pcc_dma1_ch22", "xbar_ad_divplat", base + 0x60, 30); + clks[IMX8ULP_CLK_DMA1_CH23] = imx_clk_hw_gate("pcc_dma1_ch23", "xbar_ad_divplat", base + 0x64, 30); + clks[IMX8ULP_CLK_DMA1_CH24] = imx_clk_hw_gate("pcc_dma1_ch24", "xbar_ad_divplat", base + 0x68, 30); + clks[IMX8ULP_CLK_DMA1_CH25] = imx_clk_hw_gate("pcc_dma1_ch25", "xbar_ad_divplat", base + 0x6c, 30); + clks[IMX8ULP_CLK_DMA1_CH26] = imx_clk_hw_gate("pcc_dma1_ch26", "xbar_ad_divplat", base + 0x70, 30); + clks[IMX8ULP_CLK_DMA1_CH27] = imx_clk_hw_gate("pcc_dma1_ch27", "xbar_ad_divplat", base + 0x74, 30); + clks[IMX8ULP_CLK_DMA1_CH28] = imx_clk_hw_gate("pcc_dma1_ch28", "xbar_ad_divplat", base + 0x78, 30); + clks[IMX8ULP_CLK_DMA1_CH29] = imx_clk_hw_gate("pcc_dma1_ch29", "xbar_ad_divplat", base + 0x7c, 30); + clks[IMX8ULP_CLK_DMA1_CH30] = imx_clk_hw_gate("pcc_dma1_ch30", "xbar_ad_divplat", base + 0x80, 30); + clks[IMX8ULP_CLK_DMA1_CH31] = imx_clk_hw_gate("pcc_dma1_ch31", "xbar_ad_divplat", base + 0x84, 30); + clks[IMX8ULP_CLK_MU0_B] = imx_clk_hw_gate("mu0_b", "xbar_ad_divplat", base + 0x88, 30); + clks[IMX8ULP_CLK_MU3_A] = imx_clk_hw_gate("mu3_a", "xbar_ad_divplat", base + 0x8c, 30); + + imx_check_clk_hws(clks, clk_data->num); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + if (ret) + return ret; + + imx_register_uart_clocks(1); + + /* register the pcc3 reset controller */ + return imx8ulp_pcc_reset_init(pdev, base, pcc3_resets, ARRAY_SIZE(pcc3_resets)); +} + +static int imx8ulp_clk_pcc4_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + int ret; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC4_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_PCC4_END; + clks = clk_data->hws; + + /* PCC4 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_FLEXSPI2] = imx8ulp_clk_hw_composite("flexspi2", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, true, true, base + 0x4, 1); + clks[IMX8ULP_CLK_TPM6] = imx8ulp_clk_hw_composite("tpm6", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x8, 1); + clks[IMX8ULP_CLK_TPM7] = imx8ulp_clk_hw_composite("tpm7", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0xc, 1); + clks[IMX8ULP_CLK_LPI2C6] = imx8ulp_clk_hw_composite("lpi2c6", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x10, 1); + clks[IMX8ULP_CLK_LPI2C7] = imx8ulp_clk_hw_composite("lpi2c7", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x14, 1); + clks[IMX8ULP_CLK_LPUART6] = imx8ulp_clk_hw_composite("lpuart6", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x18, 1); + clks[IMX8ULP_CLK_LPUART7] = imx8ulp_clk_hw_composite("lpuart7", pcc4_periph_bus_sels, ARRAY_SIZE(pcc4_periph_bus_sels), true, true, true, base + 0x1c, 1); + clks[IMX8ULP_CLK_SAI4] = imx8ulp_clk_hw_composite("sai4", xbar_divbus, 1, false, false, true, base + 0x20, 1); /* sai ipg, NOT from sai sel */ + clks[IMX8ULP_CLK_SAI5] = imx8ulp_clk_hw_composite("sai5", xbar_divbus, 1, false, false, true, base + 0x24, 1); /* sai ipg */ + clks[IMX8ULP_CLK_PCTLE] = imx_clk_hw_gate("pctle", "xbar_divbus", base + 0x28, 30); + clks[IMX8ULP_CLK_PCTLF] = imx_clk_hw_gate("pctlf", "xbar_divbus", base + 0x2c, 30); + clks[IMX8ULP_CLK_USDHC0] = imx8ulp_clk_hw_composite("usdhc0", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, false, true, base + 0x34, 1); + clks[IMX8ULP_CLK_USDHC1] = imx8ulp_clk_hw_composite("usdhc1", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, false, true, base + 0x38, 1); + clks[IMX8ULP_CLK_USDHC2] = imx8ulp_clk_hw_composite("usdhc2", pcc4_periph_plat_sels, ARRAY_SIZE(pcc4_periph_plat_sels), true, false, true, base + 0x3c, 1); + clks[IMX8ULP_CLK_USB0] = imx8ulp_clk_hw_composite("usb0", nic_per_divplat, 1, false, false, true, base + 0x40, 1); + clks[IMX8ULP_CLK_USB0_PHY] = imx8ulp_clk_hw_composite("usb0_phy", xbar_divbus, 1, false, false, true, base + 0x44, 1); + clks[IMX8ULP_CLK_USB1] = imx8ulp_clk_hw_composite("usb1", nic_per_divplat, 1, false, false, true, base + 0x48, 1); + clks[IMX8ULP_CLK_USB1_PHY] = imx8ulp_clk_hw_composite("usb1_phy", xbar_divbus, 1, false, false, true, base + 0x4c, 1); + clks[IMX8ULP_CLK_USB_XBAR] = imx_clk_hw_gate("usb_xbar", "xbar_divbus", base + 0x50, 30); + clks[IMX8ULP_CLK_ENET] = imx8ulp_clk_hw_composite("enet", nic_per_divplat, 1, false, false, true, base + 0x54, 1); + clks[IMX8ULP_CLK_RGPIOE] = imx_clk_hw_gate("rgpioe", "nic_per_divplat", base + 0x78, 30); + clks[IMX8ULP_CLK_RGPIOF] = imx_clk_hw_gate("rgpiof", "nic_per_divplat", base + 0x7c, 30); + + imx_check_clk_hws(clks, clk_data->num); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + if (ret) + return ret; + + /* register the pcc4 reset controller */ + return imx8ulp_pcc_reset_init(pdev, base, pcc4_resets, ARRAY_SIZE(pcc4_resets)); + +} + +static int imx8ulp_clk_pcc5_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + int ret; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, IMX8ULP_CLK_PCC5_END), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = IMX8ULP_CLK_PCC5_END; + clks = clk_data->hws; + + /* PCC5 */ + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) + return PTR_ERR(base); + + clks[IMX8ULP_CLK_DMA2_MP] = imx_clk_hw_gate("pcc_dma2_mp", "lpav_axi_div", base + 0x0, 30); + clks[IMX8ULP_CLK_DMA2_CH0] = imx_clk_hw_gate("pcc_dma2_ch0", "lpav_axi_div", base + 0x4, 30); + clks[IMX8ULP_CLK_DMA2_CH1] = imx_clk_hw_gate("pcc_dma2_ch1", "lpav_axi_div", base + 0x8, 30); + clks[IMX8ULP_CLK_DMA2_CH2] = imx_clk_hw_gate("pcc_dma2_ch2", "lpav_axi_div", base + 0xc, 30); + clks[IMX8ULP_CLK_DMA2_CH3] = imx_clk_hw_gate("pcc_dma2_ch3", "lpav_axi_div", base + 0x10, 30); + clks[IMX8ULP_CLK_DMA2_CH4] = imx_clk_hw_gate("pcc_dma2_ch4", "lpav_axi_div", base + 0x14, 30); + clks[IMX8ULP_CLK_DMA2_CH5] = imx_clk_hw_gate("pcc_dma2_ch5", "lpav_axi_div", base + 0x18, 30); + clks[IMX8ULP_CLK_DMA2_CH6] = imx_clk_hw_gate("pcc_dma2_ch6", "lpav_axi_div", base + 0x1c, 30); + clks[IMX8ULP_CLK_DMA2_CH7] = imx_clk_hw_gate("pcc_dma2_ch7", "lpav_axi_div", base + 0x20, 30); + clks[IMX8ULP_CLK_DMA2_CH8] = imx_clk_hw_gate("pcc_dma2_ch8", "lpav_axi_div", base + 0x24, 30); + clks[IMX8ULP_CLK_DMA2_CH9] = imx_clk_hw_gate("pcc_dma2_ch9", "lpav_axi_div", base + 0x28, 30); + clks[IMX8ULP_CLK_DMA2_CH10] = imx_clk_hw_gate("pcc_dma2_ch10", "lpav_axi_div", base + 0x2c, 30); + clks[IMX8ULP_CLK_DMA2_CH11] = imx_clk_hw_gate("pcc_dma2_ch11", "lpav_axi_div", base + 0x30, 30); + clks[IMX8ULP_CLK_DMA2_CH12] = imx_clk_hw_gate("pcc_dma2_ch12", "lpav_axi_div", base + 0x34, 30); + clks[IMX8ULP_CLK_DMA2_CH13] = imx_clk_hw_gate("pcc_dma2_ch13", "lpav_axi_div", base + 0x38, 30); + clks[IMX8ULP_CLK_DMA2_CH14] = imx_clk_hw_gate("pcc_dma2_ch14", "lpav_axi_div", base + 0x3c, 30); + clks[IMX8ULP_CLK_DMA2_CH15] = imx_clk_hw_gate("pcc_dma2_ch15", "lpav_axi_div", base + 0x40, 30); + clks[IMX8ULP_CLK_DMA2_CH16] = imx_clk_hw_gate("pcc_dma2_ch16", "lpav_axi_div", base + 0x44, 30); + clks[IMX8ULP_CLK_DMA2_CH17] = imx_clk_hw_gate("pcc_dma2_ch17", "lpav_axi_div", base + 0x48, 30); + clks[IMX8ULP_CLK_DMA2_CH18] = imx_clk_hw_gate("pcc_dma2_ch18", "lpav_axi_div", base + 0x4c, 30); + clks[IMX8ULP_CLK_DMA2_CH19] = imx_clk_hw_gate("pcc_dma2_ch19", "lpav_axi_div", base + 0x50, 30); + clks[IMX8ULP_CLK_DMA2_CH20] = imx_clk_hw_gate("pcc_dma2_ch20", "lpav_axi_div", base + 0x54, 30); + clks[IMX8ULP_CLK_DMA2_CH21] = imx_clk_hw_gate("pcc_dma2_ch21", "lpav_axi_div", base + 0x58, 30); + clks[IMX8ULP_CLK_DMA2_CH22] = imx_clk_hw_gate("pcc_dma2_ch22", "lpav_axi_div", base + 0x5c, 30); + clks[IMX8ULP_CLK_DMA2_CH23] = imx_clk_hw_gate("pcc_dma2_ch23", "lpav_axi_div", base + 0x60, 30); + clks[IMX8ULP_CLK_DMA2_CH24] = imx_clk_hw_gate("pcc_dma2_ch24", "lpav_axi_div", base + 0x64, 30); + clks[IMX8ULP_CLK_DMA2_CH25] = imx_clk_hw_gate("pcc_dma2_ch25", "lpav_axi_div", base + 0x68, 30); + clks[IMX8ULP_CLK_DMA2_CH26] = imx_clk_hw_gate("pcc_dma2_ch26", "lpav_axi_div", base + 0x6c, 30); + clks[IMX8ULP_CLK_DMA2_CH27] = imx_clk_hw_gate("pcc_dma2_ch27", "lpav_axi_div", base + 0x70, 30); + clks[IMX8ULP_CLK_DMA2_CH28] = imx_clk_hw_gate("pcc_dma2_ch28", "lpav_axi_div", base + 0x74, 30); + clks[IMX8ULP_CLK_DMA2_CH29] = imx_clk_hw_gate("pcc_dma2_ch29", "lpav_axi_div", base + 0x78, 30); + clks[IMX8ULP_CLK_DMA2_CH30] = imx_clk_hw_gate("pcc_dma2_ch30", "lpav_axi_div", base + 0x7c, 30); + clks[IMX8ULP_CLK_DMA2_CH31] = imx_clk_hw_gate("pcc_dma2_ch31", "lpav_axi_div", base + 0x80, 30); + + clks[IMX8ULP_CLK_AVD_SIM] = imx_clk_hw_gate("avd_sim", "lpav_bus_div", base + 0x94, 30); + clks[IMX8ULP_CLK_TPM8] = imx8ulp_clk_hw_composite("tpm8", pcc5_periph_bus_sels, ARRAY_SIZE(pcc5_periph_bus_sels), true, true, true, base + 0xa0, 1); + clks[IMX8ULP_CLK_MU2_B] = imx_clk_hw_gate("mu2_b", "lpav_bus_div", base + 0x84, 30); + clks[IMX8ULP_CLK_MU3_B] = imx_clk_hw_gate("mu3_b", "lpav_bus_div", base + 0x88, 30); + clks[IMX8ULP_CLK_SAI6] = imx8ulp_clk_hw_composite("sai6", lpav_bus_div, 1, false, false, true, base + 0xa4, 1); + clks[IMX8ULP_CLK_SAI7] = imx8ulp_clk_hw_composite("sai7", lpav_bus_div, 1, false, false, true, base + 0xa8, 1); + clks[IMX8ULP_CLK_SPDIF] = imx8ulp_clk_hw_composite("spdif", lpav_bus_div, 1, false, false, true, base + 0xac, 1); + clks[IMX8ULP_CLK_ISI] = imx8ulp_clk_hw_composite("isi", lpav_axi_div, 1, false, false, true, base + 0xb0, 1); + clks[IMX8ULP_CLK_CSI_REGS] = imx8ulp_clk_hw_composite("csi_regs", lpav_bus_div, 1, false, false, true, base + 0xb4, 1); + clks[IMX8ULP_CLK_CSI] = imx8ulp_clk_hw_composite("csi", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xbc, 1); + clks[IMX8ULP_CLK_DSI] = imx8ulp_clk_hw_composite("dsi", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xc0, 1); + clks[IMX8ULP_CLK_WDOG5] = imx8ulp_clk_hw_composite("wdog5", pcc5_periph_bus_sels, ARRAY_SIZE(pcc5_periph_bus_sels), true, true, true, base + 0xc8, 1); + clks[IMX8ULP_CLK_EPDC] = imx8ulp_clk_hw_composite("epdc", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xcc, 1); + clks[IMX8ULP_CLK_PXP] = imx8ulp_clk_hw_composite("pxp", lpav_axi_div, 1, false, false, true, base + 0xd0, 1); + clks[IMX8ULP_CLK_GPU2D] = imx8ulp_clk_hw_composite("gpu2d", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xf0, 1); + clks[IMX8ULP_CLK_GPU3D] = imx8ulp_clk_hw_composite("gpu3d", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xf4, 1); + clks[IMX8ULP_CLK_DC_NANO] = imx8ulp_clk_hw_composite("dc_nano", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0xf8, 1); + clks[IMX8ULP_CLK_CSI_CLK_UI] = imx8ulp_clk_hw_composite("csi_clk_ui", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0x10c, 1); + clks[IMX8ULP_CLK_CSI_CLK_ESC] = imx8ulp_clk_hw_composite("csi_clk_esc", pcc5_periph_plat_sels, ARRAY_SIZE(pcc5_periph_plat_sels), true, true, true, base + 0x110, 1); + clks[IMX8ULP_CLK_RGPIOD] = imx_clk_hw_gate("rgpiod", "lpav_axi_div", base + 0x114, 30); + clks[IMX8ULP_CLK_DSI_TX_ESC] = imx_clk_hw_fixed_factor("mipi_dsi_tx_esc", "dsi", 1, 4); + + imx_check_clk_hws(clks, clk_data->num); + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); + if (ret) + return ret; + + /* register the pcc5 reset controller */ + return imx8ulp_pcc_reset_init(pdev, base, pcc5_resets, ARRAY_SIZE(pcc5_resets)); +} + +static int imx8ulp_clk_probe(struct platform_device *pdev) +{ + int (*probe)(struct platform_device *pdev); + + probe = of_device_get_match_data(&pdev->dev); + + if (probe) + return probe(pdev); + + return 0; +} + +static const struct of_device_id imx8ulp_clk_dt_ids[] = { + { .compatible = "fsl,imx8ulp-pcc3", .data = imx8ulp_clk_pcc3_init }, + { .compatible = "fsl,imx8ulp-pcc4", .data = imx8ulp_clk_pcc4_init }, + { .compatible = "fsl,imx8ulp-pcc5", .data = imx8ulp_clk_pcc5_init }, + { .compatible = "fsl,imx8ulp-cgc2", .data = imx8ulp_clk_cgc2_init }, + { .compatible = "fsl,imx8ulp-cgc1", .data = imx8ulp_clk_cgc1_init }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx8ulp_clk_dt_ids); + +static struct platform_driver imx8ulp_clk_driver = { + .probe = imx8ulp_clk_probe, + .driver = { + .name = KBUILD_MODNAME, + .suppress_bind_attrs = true, + .of_match_table = imx8ulp_clk_dt_ids, + }, +}; +module_platform_driver(imx8ulp_clk_driver); + +MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); +MODULE_DESCRIPTION("NXP i.MX8ULP clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c new file mode 100644 index 000000000..61d9f9bf8 --- /dev/null +++ b/drivers/clk/imx/clk-imx93.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2021 NXP. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <dt-bindings/clock/imx93-clock.h> + +#include "clk.h" + +enum clk_sel { + LOW_SPEED_IO_SEL, + NON_IO_SEL, + FAST_SEL, + AUDIO_SEL, + VIDEO_SEL, + TPM_SEL, + CKO1_SEL, + CKO2_SEL, + MISC_SEL, + MAX_SEL +}; + +static u32 share_count_sai1; +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_mub; + +static const char *parent_names[MAX_SEL][4] = { + {"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "video_pll"}, + {"osc_24m", "sys_pll_pfd0_div2", "sys_pll_pfd1_div2", "sys_pll_pfd2_div2"}, + {"osc_24m", "sys_pll_pfd0", "sys_pll_pfd1", "sys_pll_pfd2"}, + {"osc_24m", "audio_pll", "video_pll", "clk_ext1"}, + {"osc_24m", "audio_pll", "video_pll", "sys_pll_pfd0"}, + {"osc_24m", "sys_pll_pfd0", "audio_pll", "clk_ext1"}, + {"osc_24m", "sys_pll_pfd0", "sys_pll_pfd1", "audio_pll"}, + {"osc_24m", "sys_pll_pfd0", "sys_pll_pfd1", "video_pll"}, + {"osc_24m", "audio_pll", "video_pll", "sys_pll_pfd2"}, +}; + +static const struct imx93_clk_root { + u32 clk; + char *name; + u32 off; + enum clk_sel sel; + unsigned long flags; +} root_array[] = { + /* a55/m33/bus critical clk for system run */ + { IMX93_CLK_A55_PERIPH, "a55_periph_root", 0x0000, FAST_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_A55_MTR_BUS, "a55_mtr_bus_root", 0x0080, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_A55, "a55_root", 0x0100, FAST_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_M33, "m33_root", 0x0180, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_BUS_WAKEUP, "bus_wakeup_root", 0x0280, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_BUS_AON, "bus_aon_root", 0x0300, LOW_SPEED_IO_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_WAKEUP_AXI, "wakeup_axi_root", 0x0380, FAST_SEL, CLK_IS_CRITICAL }, + { IMX93_CLK_SWO_TRACE, "swo_trace_root", 0x0400, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_M33_SYSTICK, "m33_systick_root", 0x0480, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_FLEXIO1, "flexio1_root", 0x0500, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_FLEXIO2, "flexio2_root", 0x0580, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPIT1, "lpit1_root", 0x0600, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPIT2, "lpit2_root", 0x0680, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPTMR1, "lptmr1_root", 0x0700, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPTMR2, "lptmr2_root", 0x0780, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_TPM1, "tpm1_root", 0x0800, TPM_SEL, }, + { IMX93_CLK_TPM2, "tpm2_root", 0x0880, TPM_SEL, }, + { IMX93_CLK_TPM3, "tpm3_root", 0x0900, TPM_SEL, }, + { IMX93_CLK_TPM4, "tpm4_root", 0x0980, TPM_SEL, }, + { IMX93_CLK_TPM5, "tpm5_root", 0x0a00, TPM_SEL, }, + { IMX93_CLK_TPM6, "tpm6_root", 0x0a80, TPM_SEL, }, + { IMX93_CLK_FLEXSPI1, "flexspi1_root", 0x0b00, FAST_SEL, }, + { IMX93_CLK_CAN1, "can1_root", 0x0b80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_CAN2, "can2_root", 0x0c00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART1, "lpuart1_root", 0x0c80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART2, "lpuart2_root", 0x0d00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART3, "lpuart3_root", 0x0d80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART4, "lpuart4_root", 0x0e00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART5, "lpuart5_root", 0x0e80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART6, "lpuart6_root", 0x0f00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART7, "lpuart7_root", 0x0f80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPUART8, "lpuart8_root", 0x1000, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C1, "lpi2c1_root", 0x1080, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C2, "lpi2c2_root", 0x1100, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C3, "lpi2c3_root", 0x1180, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C4, "lpi2c4_root", 0x1200, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C5, "lpi2c5_root", 0x1280, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C6, "lpi2c6_root", 0x1300, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C7, "lpi2c7_root", 0x1380, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPI2C8, "lpi2c8_root", 0x1400, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI1, "lpspi1_root", 0x1480, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI2, "lpspi2_root", 0x1500, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI3, "lpspi3_root", 0x1580, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI4, "lpspi4_root", 0x1600, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI5, "lpspi5_root", 0x1680, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI6, "lpspi6_root", 0x1700, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI7, "lpspi7_root", 0x1780, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_LPSPI8, "lpspi8_root", 0x1800, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C1, "i3c1_root", 0x1880, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C2, "i3c2_root", 0x1900, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_USDHC1, "usdhc1_root", 0x1980, FAST_SEL, }, + { IMX93_CLK_USDHC2, "usdhc2_root", 0x1a00, FAST_SEL, }, + { IMX93_CLK_USDHC3, "usdhc3_root", 0x1a80, FAST_SEL, }, + { IMX93_CLK_SAI1, "sai1_root", 0x1b00, AUDIO_SEL, }, + { IMX93_CLK_SAI2, "sai2_root", 0x1b80, AUDIO_SEL, }, + { IMX93_CLK_SAI3, "sai3_root", 0x1c00, AUDIO_SEL, }, + { IMX93_CLK_CCM_CKO1, "ccm_cko1_root", 0x1c80, CKO1_SEL, }, + { IMX93_CLK_CCM_CKO2, "ccm_cko2_root", 0x1d00, CKO2_SEL, }, + { IMX93_CLK_CCM_CKO3, "ccm_cko3_root", 0x1d80, CKO1_SEL, }, + { IMX93_CLK_CCM_CKO4, "ccm_cko4_root", 0x1e00, CKO2_SEL, }, + { IMX93_CLK_HSIO, "hsio_root", 0x1e80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_HSIO_USB_TEST_60M, "hsio_usb_test_60m_root", 0x1f00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_HSIO_ACSCAN_80M, "hsio_acscan_80m_root", 0x1f80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_HSIO_ACSCAN_480M, "hsio_acscan_480m_root", 0x2000, MISC_SEL, }, + { IMX93_CLK_ML_APB, "ml_apb_root", 0x2180, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_ML, "ml_root", 0x2200, FAST_SEL, }, + { IMX93_CLK_MEDIA_AXI, "media_axi_root", 0x2280, FAST_SEL, }, + { IMX93_CLK_MEDIA_APB, "media_apb_root", 0x2300, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_MEDIA_LDB, "media_ldb_root", 0x2380, VIDEO_SEL, }, + { IMX93_CLK_MEDIA_DISP_PIX, "media_disp_pix_root", 0x2400, VIDEO_SEL, }, + { IMX93_CLK_CAM_PIX, "cam_pix_root", 0x2480, VIDEO_SEL, }, + { IMX93_CLK_MIPI_TEST_BYTE, "mipi_test_byte_root", 0x2500, VIDEO_SEL, }, + { IMX93_CLK_MIPI_PHY_CFG, "mipi_phy_cfg_root", 0x2580, VIDEO_SEL, }, + { IMX93_CLK_ADC, "adc_root", 0x2700, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_PDM, "pdm_root", 0x2780, AUDIO_SEL, }, + { IMX93_CLK_TSTMR1, "tstmr1_root", 0x2800, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_TSTMR2, "tstmr2_root", 0x2880, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_MQS1, "mqs1_root", 0x2900, AUDIO_SEL, }, + { IMX93_CLK_MQS2, "mqs2_root", 0x2980, AUDIO_SEL, }, + { IMX93_CLK_AUDIO_XCVR, "audio_xcvr_root", 0x2a00, NON_IO_SEL, }, + { IMX93_CLK_SPDIF, "spdif_root", 0x2a80, AUDIO_SEL, }, + { IMX93_CLK_ENET, "enet_root", 0x2b00, NON_IO_SEL, }, + { IMX93_CLK_ENET_TIMER1, "enet_timer1_root", 0x2b80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_ENET_TIMER2, "enet_timer2_root", 0x2c00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_ENET_REF, "enet_ref_root", 0x2c80, NON_IO_SEL, }, + { IMX93_CLK_ENET_REF_PHY, "enet_ref_phy_root", 0x2d00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C1_SLOW, "i3c1_slow_root", 0x2d80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_I3C2_SLOW, "i3c2_slow_root", 0x2e00, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_USB_PHY_BURUNIN, "usb_phy_root", 0x2e80, LOW_SPEED_IO_SEL, }, + { IMX93_CLK_PAL_CAME_SCAN, "pal_came_scan_root", 0x2f00, MISC_SEL, } +}; + +static const struct imx93_clk_ccgr { + u32 clk; + char *name; + char *parent_name; + u32 off; + unsigned long flags; + u32 *shared_count; +} ccgr_array[] = { + { IMX93_CLK_A55_GATE, "a55", "a55_root", 0x8000, }, + /* M33 critical clk for system run */ + { IMX93_CLK_CM33_GATE, "cm33", "m33_root", 0x8040, CLK_IS_CRITICAL }, + { IMX93_CLK_ADC1_GATE, "adc1", "adc_root", 0x82c0, }, + { IMX93_CLK_WDOG1_GATE, "wdog1", "osc_24m", 0x8300, }, + { IMX93_CLK_WDOG2_GATE, "wdog2", "osc_24m", 0x8340, }, + { IMX93_CLK_WDOG3_GATE, "wdog3", "osc_24m", 0x8380, }, + { IMX93_CLK_WDOG4_GATE, "wdog4", "osc_24m", 0x83c0, }, + { IMX93_CLK_WDOG5_GATE, "wdog5", "osc_24m", 0x8400, }, + { IMX93_CLK_SEMA1_GATE, "sema1", "bus_aon_root", 0x8440, }, + { IMX93_CLK_SEMA2_GATE, "sema2", "bus_wakeup_root", 0x8480, }, + { IMX93_CLK_MU1_A_GATE, "mu1_a", "bus_aon_root", 0x84c0, CLK_IGNORE_UNUSED }, + { IMX93_CLK_MU2_A_GATE, "mu2_a", "bus_wakeup_root", 0x84c0, CLK_IGNORE_UNUSED }, + { IMX93_CLK_MU1_B_GATE, "mu1_b", "bus_aon_root", 0x8500, 0, &share_count_mub }, + { IMX93_CLK_MU2_B_GATE, "mu2_b", "bus_wakeup_root", 0x8500, 0, &share_count_mub }, + { IMX93_CLK_EDMA1_GATE, "edma1", "m33_root", 0x8540, }, + { IMX93_CLK_EDMA2_GATE, "edma2", "wakeup_axi_root", 0x8580, }, + { IMX93_CLK_FLEXSPI1_GATE, "flexspi1", "flexspi1_root", 0x8640, }, + { IMX93_CLK_GPIO1_GATE, "gpio1", "m33_root", 0x8880, }, + { IMX93_CLK_GPIO2_GATE, "gpio2", "bus_wakeup_root", 0x88c0, }, + { IMX93_CLK_GPIO3_GATE, "gpio3", "bus_wakeup_root", 0x8900, }, + { IMX93_CLK_GPIO4_GATE, "gpio4", "bus_wakeup_root", 0x8940, }, + { IMX93_CLK_FLEXIO1_GATE, "flexio1", "flexio1_root", 0x8980, }, + { IMX93_CLK_FLEXIO2_GATE, "flexio2", "flexio2_root", 0x89c0, }, + { IMX93_CLK_LPIT1_GATE, "lpit1", "lpit1_root", 0x8a00, }, + { IMX93_CLK_LPIT2_GATE, "lpit2", "lpit2_root", 0x8a40, }, + { IMX93_CLK_LPTMR1_GATE, "lptmr1", "lptmr1_root", 0x8a80, }, + { IMX93_CLK_LPTMR2_GATE, "lptmr2", "lptmr2_root", 0x8ac0, }, + { IMX93_CLK_TPM1_GATE, "tpm1", "tpm1_root", 0x8b00, }, + { IMX93_CLK_TPM2_GATE, "tpm2", "tpm2_root", 0x8b40, }, + { IMX93_CLK_TPM3_GATE, "tpm3", "tpm3_root", 0x8b80, }, + { IMX93_CLK_TPM4_GATE, "tpm4", "tpm4_root", 0x8bc0, }, + { IMX93_CLK_TPM5_GATE, "tpm5", "tpm5_root", 0x8c00, }, + { IMX93_CLK_TPM6_GATE, "tpm6", "tpm6_root", 0x8c40, }, + { IMX93_CLK_CAN1_GATE, "can1", "can1_root", 0x8c80, }, + { IMX93_CLK_CAN2_GATE, "can2", "can2_root", 0x8cc0, }, + { IMX93_CLK_LPUART1_GATE, "lpuart1", "lpuart1_root", 0x8d00, }, + { IMX93_CLK_LPUART2_GATE, "lpuart2", "lpuart2_root", 0x8d40, }, + { IMX93_CLK_LPUART3_GATE, "lpuart3", "lpuart3_root", 0x8d80, }, + { IMX93_CLK_LPUART4_GATE, "lpuart4", "lpuart4_root", 0x8dc0, }, + { IMX93_CLK_LPUART5_GATE, "lpuart5", "lpuart5_root", 0x8e00, }, + { IMX93_CLK_LPUART6_GATE, "lpuart6", "lpuart6_root", 0x8e40, }, + { IMX93_CLK_LPUART7_GATE, "lpuart7", "lpuart7_root", 0x8e80, }, + { IMX93_CLK_LPUART8_GATE, "lpuart8", "lpuart8_root", 0x8ec0, }, + { IMX93_CLK_LPI2C1_GATE, "lpi2c1", "lpi2c1_root", 0x8f00, }, + { IMX93_CLK_LPI2C2_GATE, "lpi2c2", "lpi2c2_root", 0x8f40, }, + { IMX93_CLK_LPI2C3_GATE, "lpi2c3", "lpi2c3_root", 0x8f80, }, + { IMX93_CLK_LPI2C4_GATE, "lpi2c4", "lpi2c4_root", 0x8fc0, }, + { IMX93_CLK_LPI2C5_GATE, "lpi2c5", "lpi2c5_root", 0x9000, }, + { IMX93_CLK_LPI2C6_GATE, "lpi2c6", "lpi2c6_root", 0x9040, }, + { IMX93_CLK_LPI2C7_GATE, "lpi2c7", "lpi2c7_root", 0x9080, }, + { IMX93_CLK_LPI2C8_GATE, "lpi2c8", "lpi2c8_root", 0x90c0, }, + { IMX93_CLK_LPSPI1_GATE, "lpspi1", "lpspi1_root", 0x9100, }, + { IMX93_CLK_LPSPI2_GATE, "lpspi2", "lpspi2_root", 0x9140, }, + { IMX93_CLK_LPSPI3_GATE, "lpspi3", "lpspi3_root", 0x9180, }, + { IMX93_CLK_LPSPI4_GATE, "lpspi4", "lpspi4_root", 0x91c0, }, + { IMX93_CLK_LPSPI5_GATE, "lpspi5", "lpspi5_root", 0x9200, }, + { IMX93_CLK_LPSPI6_GATE, "lpspi6", "lpspi6_root", 0x9240, }, + { IMX93_CLK_LPSPI7_GATE, "lpspi7", "lpspi7_root", 0x9280, }, + { IMX93_CLK_LPSPI8_GATE, "lpspi8", "lpspi8_root", 0x92c0, }, + { IMX93_CLK_I3C1_GATE, "i3c1", "i3c1_root", 0x9300, }, + { IMX93_CLK_I3C2_GATE, "i3c2", "i3c2_root", 0x9340, }, + { IMX93_CLK_USDHC1_GATE, "usdhc1", "usdhc1_root", 0x9380, }, + { IMX93_CLK_USDHC2_GATE, "usdhc2", "usdhc2_root", 0x93c0, }, + { IMX93_CLK_USDHC3_GATE, "usdhc3", "usdhc3_root", 0x9400, }, + { IMX93_CLK_SAI1_GATE, "sai1", "sai1_root", 0x9440, 0, &share_count_sai1}, + { IMX93_CLK_SAI1_IPG, "sai1_ipg_clk", "bus_aon_root", 0x9440, 0, &share_count_sai1}, + { IMX93_CLK_SAI2_GATE, "sai2", "sai2_root", 0x9480, 0, &share_count_sai2}, + { IMX93_CLK_SAI2_IPG, "sai2_ipg_clk", "bus_wakeup_root", 0x9480, 0, &share_count_sai2}, + { IMX93_CLK_SAI3_GATE, "sai3", "sai3_root", 0x94c0, 0, &share_count_sai3}, + { IMX93_CLK_SAI3_IPG, "sai3_ipg_clk", "bus_wakeup_root", 0x94c0, 0, &share_count_sai3}, + { IMX93_CLK_MIPI_CSI_GATE, "mipi_csi", "media_apb_root", 0x9580, }, + { IMX93_CLK_MIPI_DSI_GATE, "mipi_dsi", "media_apb_root", 0x95c0, }, + { IMX93_CLK_LVDS_GATE, "lvds", "media_ldb_root", 0x9600, }, + { IMX93_CLK_LCDIF_GATE, "lcdif", "media_apb_root", 0x9640, }, + { IMX93_CLK_PXP_GATE, "pxp", "media_apb_root", 0x9680, }, + { IMX93_CLK_ISI_GATE, "isi", "media_apb_root", 0x96c0, }, + { IMX93_CLK_NIC_MEDIA_GATE, "nic_media", "media_axi_root", 0x9700, }, + { IMX93_CLK_USB_CONTROLLER_GATE, "usb_controller", "hsio_root", 0x9a00, }, + { IMX93_CLK_USB_TEST_60M_GATE, "usb_test_60m", "hsio_usb_test_60m_root", 0x9a40, }, + { IMX93_CLK_HSIO_TROUT_24M_GATE, "hsio_trout_24m", "osc_24m", 0x9a80, }, + { IMX93_CLK_PDM_GATE, "pdm", "pdm_root", 0x9ac0, }, + { IMX93_CLK_MQS1_GATE, "mqs1", "sai1_root", 0x9b00, }, + { IMX93_CLK_MQS2_GATE, "mqs2", "sai3_root", 0x9b40, }, + { IMX93_CLK_AUD_XCVR_GATE, "aud_xcvr", "audio_xcvr_root", 0x9b80, }, + { IMX93_CLK_SPDIF_GATE, "spdif", "spdif_root", 0x9c00, }, + { IMX93_CLK_HSIO_32K_GATE, "hsio_32k", "osc_32k", 0x9dc0, }, + { IMX93_CLK_ENET1_GATE, "enet1", "wakeup_axi_root", 0x9e00, }, + { IMX93_CLK_ENET_QOS_GATE, "enet_qos", "wakeup_axi_root", 0x9e40, }, + { IMX93_CLK_SYS_CNT_GATE, "sys_cnt", "osc_24m", 0x9e80, }, + { IMX93_CLK_TSTMR1_GATE, "tstmr1", "bus_aon_root", 0x9ec0, }, + { IMX93_CLK_TSTMR2_GATE, "tstmr2", "bus_wakeup_root", 0x9f00, }, + { IMX93_CLK_TMC_GATE, "tmc", "osc_24m", 0x9f40, }, + { IMX93_CLK_PMRO_GATE, "pmro", "osc_24m", 0x9f80, } +}; + +static struct clk_hw_onecell_data *clk_hw_data; +static struct clk_hw **clks; + +static int imx93_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct imx93_clk_root *root; + const struct imx93_clk_ccgr *ccgr; + void __iomem *base, *anatop_base; + int i, ret; + + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, + IMX93_CLK_END), GFP_KERNEL); + if (!clk_hw_data) + return -ENOMEM; + + clk_hw_data->num = IMX93_CLK_END; + clks = clk_hw_data->hws; + + clks[IMX93_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + clks[IMX93_CLK_24M] = imx_obtain_fixed_clk_hw(np, "osc_24m"); + clks[IMX93_CLK_32K] = imx_obtain_fixed_clk_hw(np, "osc_32k"); + clks[IMX93_CLK_EXT1] = imx_obtain_fixed_clk_hw(np, "clk_ext1"); + + clks[IMX93_CLK_SYS_PLL_PFD0] = imx_clk_hw_fixed("sys_pll_pfd0", 1000000000); + clks[IMX93_CLK_SYS_PLL_PFD0_DIV2] = imx_clk_hw_fixed_factor("sys_pll_pfd0_div2", + "sys_pll_pfd0", 1, 2); + clks[IMX93_CLK_SYS_PLL_PFD1] = imx_clk_hw_fixed("sys_pll_pfd1", 800000000); + clks[IMX93_CLK_SYS_PLL_PFD1_DIV2] = imx_clk_hw_fixed_factor("sys_pll_pfd1_div2", + "sys_pll_pfd1", 1, 2); + clks[IMX93_CLK_SYS_PLL_PFD2] = imx_clk_hw_fixed("sys_pll_pfd2", 625000000); + clks[IMX93_CLK_SYS_PLL_PFD2_DIV2] = imx_clk_hw_fixed_factor("sys_pll_pfd2_div2", + "sys_pll_pfd2", 1, 2); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx93-anatop"); + anatop_base = devm_of_iomap(dev, np, 0, NULL); + of_node_put(np); + if (WARN_ON(IS_ERR(anatop_base))) { + ret = PTR_ERR(anatop_base); + goto unregister_hws; + } + + clks[IMX93_CLK_AUDIO_PLL] = imx_clk_fracn_gppll("audio_pll", "osc_24m", anatop_base + 0x1200, + &imx_fracn_gppll); + clks[IMX93_CLK_VIDEO_PLL] = imx_clk_fracn_gppll("video_pll", "osc_24m", anatop_base + 0x1400, + &imx_fracn_gppll); + + np = dev->of_node; + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) { + ret = PTR_ERR(base); + goto unregister_hws; + } + + for (i = 0; i < ARRAY_SIZE(root_array); i++) { + root = &root_array[i]; + clks[root->clk] = imx93_clk_composite_flags(root->name, + parent_names[root->sel], + 4, base + root->off, 3, + root->flags); + } + + for (i = 0; i < ARRAY_SIZE(ccgr_array); i++) { + ccgr = &ccgr_array[i]; + clks[ccgr->clk] = imx93_clk_gate(NULL, ccgr->name, ccgr->parent_name, + ccgr->flags, base + ccgr->off, 0, 1, 1, 3, + ccgr->shared_count); + } + + imx_check_clk_hws(clks, IMX93_CLK_END); + + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + if (ret < 0) { + dev_err(dev, "failed to register clks for i.MX93\n"); + goto unregister_hws; + } + + return 0; + +unregister_hws: + imx_unregister_hw_clocks(clks, IMX93_CLK_END); + + return ret; +} + +static const struct of_device_id imx93_clk_of_match[] = { + { .compatible = "fsl,imx93-ccm" }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx93_clk_of_match); + +static struct platform_driver imx93_clk_driver = { + .probe = imx93_clocks_probe, + .driver = { + .name = "imx93-ccm", + .suppress_bind_attrs = true, + .of_match_table = imx93_clk_of_match, + }, +}; +module_platform_driver(imx93_clk_driver); + +MODULE_DESCRIPTION("NXP i.MX93 clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk-imxrt1050.c b/drivers/clk/imx/clk-imxrt1050.c new file mode 100644 index 000000000..64d8b65a8 --- /dev/null +++ b/drivers/clk/imx/clk-imxrt1050.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright (C) 2021 + * Author(s): + * Jesse Taube <Mr.Bossman075@gmail.com> + * Giulio Benetti <giulio.benetti@benettiengineering.com> + */ +#include <linux/clk.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <dt-bindings/clock/imxrt1050-clock.h> + +#include "clk.h" + +static const char * const pll_ref_sels[] = {"osc", "dummy", }; +static const char * const per_sels[] = {"ipg_pdof", "osc", }; +static const char * const pll1_bypass_sels[] = {"pll1_arm", "pll1_arm_ref_sel", }; +static const char * const pll2_bypass_sels[] = {"pll2_sys", "pll2_sys_ref_sel", }; +static const char * const pll3_bypass_sels[] = {"pll3_usb_otg", "pll3_usb_otg_ref_sel", }; +static const char * const pll5_bypass_sels[] = {"pll5_video", "pll5_video_ref_sel", }; +static const char *const pre_periph_sels[] = { + "pll2_sys", "pll2_pfd2_396m", "pll2_pfd0_352m", "arm_podf", }; +static const char *const periph_sels[] = { "pre_periph_sel", "todo", }; +static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", }; +static const char *const lpuart_sels[] = { "pll3_80m", "osc", }; +static const char *const lcdif_sels[] = { + "pll2_sys", "pll3_pfd3_454_74m", "pll5_video", "pll2_pfd0_352m", + "pll2_pfd1_594m", "pll3_pfd1_664_62m", }; +static const char *const semc_alt_sels[] = { "pll2_pfd2_396m", "pll3_pfd1_664_62m", }; +static const char *const semc_sels[] = { "periph_sel", "semc_alt_sel", }; + +static struct clk_hw **hws; +static struct clk_hw_onecell_data *clk_hw_data; + +static int imxrt1050_clocks_probe(struct platform_device *pdev) +{ + void __iomem *ccm_base; + void __iomem *pll_base; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *anp; + int ret; + + clk_hw_data = devm_kzalloc(dev, struct_size(clk_hw_data, hws, + IMXRT1050_CLK_END), GFP_KERNEL); + if (WARN_ON(!clk_hw_data)) + return -ENOMEM; + + clk_hw_data->num = IMXRT1050_CLK_END; + hws = clk_hw_data->hws; + + hws[IMXRT1050_CLK_OSC] = imx_obtain_fixed_clk_hw(np, "osc"); + + anp = of_find_compatible_node(NULL, NULL, "fsl,imxrt-anatop"); + pll_base = devm_of_iomap(dev, anp, 0, NULL); + of_node_put(anp); + if (WARN_ON(IS_ERR(pll_base))) { + ret = PTR_ERR(pll_base); + goto unregister_hws; + } + + /* Anatop clocks */ + hws[IMXRT1050_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0UL); + + hws[IMXRT1050_CLK_PLL1_REF_SEL] = imx_clk_hw_mux("pll1_arm_ref_sel", + pll_base + 0x0, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMXRT1050_CLK_PLL2_REF_SEL] = imx_clk_hw_mux("pll2_sys_ref_sel", + pll_base + 0x30, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMXRT1050_CLK_PLL3_REF_SEL] = imx_clk_hw_mux("pll3_usb_otg_ref_sel", + pll_base + 0x10, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + hws[IMXRT1050_CLK_PLL5_REF_SEL] = imx_clk_hw_mux("pll5_video_ref_sel", + pll_base + 0xa0, 14, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + + hws[IMXRT1050_CLK_PLL1_ARM] = imx_clk_hw_pllv3(IMX_PLLV3_SYS, "pll1_arm", + "pll1_arm_ref_sel", pll_base + 0x0, 0x7f); + hws[IMXRT1050_CLK_PLL2_SYS] = imx_clk_hw_pllv3(IMX_PLLV3_GENERIC, "pll2_sys", + "pll2_sys_ref_sel", pll_base + 0x30, 0x1); + hws[IMXRT1050_CLK_PLL3_USB_OTG] = imx_clk_hw_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", + "pll3_usb_otg_ref_sel", pll_base + 0x10, 0x1); + hws[IMXRT1050_CLK_PLL5_VIDEO] = imx_clk_hw_pllv3(IMX_PLLV3_AV, "pll5_video", + "pll5_video_ref_sel", pll_base + 0xa0, 0x7f); + + /* PLL bypass out */ + hws[IMXRT1050_CLK_PLL1_BYPASS] = imx_clk_hw_mux_flags("pll1_bypass", pll_base + 0x0, 16, 1, + pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMXRT1050_CLK_PLL2_BYPASS] = imx_clk_hw_mux_flags("pll2_bypass", pll_base + 0x30, 16, 1, + pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMXRT1050_CLK_PLL3_BYPASS] = imx_clk_hw_mux_flags("pll3_bypass", pll_base + 0x10, 16, 1, + pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + hws[IMXRT1050_CLK_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", pll_base + 0xa0, 16, 1, + pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + + hws[IMXRT1050_CLK_VIDEO_POST_DIV_SEL] = imx_clk_hw_divider("video_post_div_sel", + "pll5_video", pll_base + 0xa0, 19, 2); + hws[IMXRT1050_CLK_VIDEO_DIV] = imx_clk_hw_divider("video_div", + "video_post_div_sel", pll_base + 0x170, 30, 2); + + hws[IMXRT1050_CLK_PLL3_80M] = imx_clk_hw_fixed_factor("pll3_80m", "pll3_usb_otg", 1, 6); + + hws[IMXRT1050_CLK_PLL2_PFD0_352M] = imx_clk_hw_pfd("pll2_pfd0_352m", "pll2_sys", pll_base + 0x100, 0); + hws[IMXRT1050_CLK_PLL2_PFD1_594M] = imx_clk_hw_pfd("pll2_pfd1_594m", "pll2_sys", pll_base + 0x100, 1); + hws[IMXRT1050_CLK_PLL2_PFD2_396M] = imx_clk_hw_pfd("pll2_pfd2_396m", "pll2_sys", pll_base + 0x100, 2); + hws[IMXRT1050_CLK_PLL3_PFD1_664_62M] = imx_clk_hw_pfd("pll3_pfd1_664_62m", "pll3_usb_otg", pll_base + 0xf0, 1); + hws[IMXRT1050_CLK_PLL3_PFD3_454_74M] = imx_clk_hw_pfd("pll3_pfd3_454_74m", "pll3_usb_otg", pll_base + 0xf0, 3); + + /* CCM clocks */ + ccm_base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(ccm_base))) { + ret = PTR_ERR(ccm_base); + goto unregister_hws; + } + + hws[IMXRT1050_CLK_ARM_PODF] = imx_clk_hw_divider("arm_podf", "pll1_arm", ccm_base + 0x10, 0, 3); + hws[IMXRT1050_CLK_PRE_PERIPH_SEL] = imx_clk_hw_mux("pre_periph_sel", ccm_base + 0x18, 18, 2, + pre_periph_sels, ARRAY_SIZE(pre_periph_sels)); + hws[IMXRT1050_CLK_PERIPH_SEL] = imx_clk_hw_mux("periph_sel", ccm_base + 0x14, 25, 1, + periph_sels, ARRAY_SIZE(periph_sels)); + hws[IMXRT1050_CLK_USDHC1_SEL] = imx_clk_hw_mux("usdhc1_sel", ccm_base + 0x1c, 16, 1, + usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMXRT1050_CLK_USDHC2_SEL] = imx_clk_hw_mux("usdhc2_sel", ccm_base + 0x1c, 17, 1, + usdhc_sels, ARRAY_SIZE(usdhc_sels)); + hws[IMXRT1050_CLK_LPUART_SEL] = imx_clk_hw_mux("lpuart_sel", ccm_base + 0x24, 6, 1, + lpuart_sels, ARRAY_SIZE(lpuart_sels)); + hws[IMXRT1050_CLK_LCDIF_SEL] = imx_clk_hw_mux("lcdif_sel", ccm_base + 0x38, 15, 3, + lcdif_sels, ARRAY_SIZE(lcdif_sels)); + hws[IMXRT1050_CLK_PER_CLK_SEL] = imx_clk_hw_mux("per_sel", ccm_base + 0x1C, 6, 1, + per_sels, ARRAY_SIZE(per_sels)); + hws[IMXRT1050_CLK_SEMC_ALT_SEL] = imx_clk_hw_mux("semc_alt_sel", ccm_base + 0x14, 7, 1, + semc_alt_sels, ARRAY_SIZE(semc_alt_sels)); + hws[IMXRT1050_CLK_SEMC_SEL] = imx_clk_hw_mux_flags("semc_sel", ccm_base + 0x14, 6, 1, + semc_sels, ARRAY_SIZE(semc_sels), CLK_IS_CRITICAL); + + hws[IMXRT1050_CLK_AHB_PODF] = imx_clk_hw_divider("ahb", "periph_sel", ccm_base + 0x14, 10, 3); + hws[IMXRT1050_CLK_IPG_PDOF] = imx_clk_hw_divider("ipg", "ahb", ccm_base + 0x14, 8, 2); + hws[IMXRT1050_CLK_PER_PDOF] = imx_clk_hw_divider("per", "per_sel", ccm_base + 0x1C, 0, 5); + + hws[IMXRT1050_CLK_USDHC1_PODF] = imx_clk_hw_divider("usdhc1_podf", "usdhc1_sel", ccm_base + 0x24, 11, 3); + hws[IMXRT1050_CLK_USDHC2_PODF] = imx_clk_hw_divider("usdhc2_podf", "usdhc2_sel", ccm_base + 0x24, 16, 3); + hws[IMXRT1050_CLK_LPUART_PODF] = imx_clk_hw_divider("lpuart_podf", "lpuart_sel", ccm_base + 0x24, 0, 6); + hws[IMXRT1050_CLK_LCDIF_PRED] = imx_clk_hw_divider("lcdif_pred", "lcdif_sel", ccm_base + 0x38, 12, 3); + hws[IMXRT1050_CLK_LCDIF_PODF] = imx_clk_hw_divider("lcdif_podf", "lcdif_pred", ccm_base + 0x18, 23, 3); + + hws[IMXRT1050_CLK_USDHC1] = imx_clk_hw_gate2("usdhc1", "usdhc1_podf", ccm_base + 0x80, 2); + hws[IMXRT1050_CLK_USDHC2] = imx_clk_hw_gate2("usdhc2", "usdhc2_podf", ccm_base + 0x80, 4); + hws[IMXRT1050_CLK_LPUART1] = imx_clk_hw_gate2("lpuart1", "lpuart_podf", ccm_base + 0x7c, 24); + hws[IMXRT1050_CLK_LCDIF_APB] = imx_clk_hw_gate2("lcdif", "lcdif_podf", ccm_base + 0x70, 28); + hws[IMXRT1050_CLK_DMA] = imx_clk_hw_gate("dma", "ipg", ccm_base + 0x7C, 6); + hws[IMXRT1050_CLK_DMA_MUX] = imx_clk_hw_gate("dmamux0", "ipg", ccm_base + 0x7C, 7); + imx_check_clk_hws(hws, IMXRT1050_CLK_END); + + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data); + if (ret < 0) { + dev_err(dev, "Failed to register clks for i.MXRT1050.\n"); + goto unregister_hws; + } + return 0; + +unregister_hws: + imx_unregister_hw_clocks(hws, IMXRT1050_CLK_END); + return ret; +} +static const struct of_device_id imxrt1050_clk_of_match[] = { + { .compatible = "fsl,imxrt1050-ccm" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imxrt1050_clk_of_match); + +static struct platform_driver imxrt1050_clk_driver = { + .probe = imxrt1050_clocks_probe, + .driver = { + .name = "imxrt1050-ccm", + .of_match_table = imxrt1050_clk_of_match, + }, +}; +module_platform_driver(imxrt1050_clk_driver); diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c new file mode 100644 index 000000000..dd5abd09f --- /dev/null +++ b/drivers/clk/imx/clk-lpcg-scu.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "clk-scu.h" + +static DEFINE_SPINLOCK(imx_lpcg_scu_lock); + +#define CLK_GATE_SCU_LPCG_MASK 0x3 +#define CLK_GATE_SCU_LPCG_HW_SEL BIT(0) +#define CLK_GATE_SCU_LPCG_SW_SEL BIT(1) + +/* + * struct clk_lpcg_scu - Description of LPCG clock + * + * @hw: clk_hw of this LPCG + * @reg: register of this LPCG clock + * @bit_idx: bit index of this LPCG clock + * @hw_gate: HW auto gate enable + * + * This structure describes one LPCG clock + */ +struct clk_lpcg_scu { + struct clk_hw hw; + void __iomem *reg; + u8 bit_idx; + bool hw_gate; + + /* for state save&restore */ + u32 state; +}; + +#define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw) + +static int clk_lpcg_scu_enable(struct clk_hw *hw) +{ + struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); + unsigned long flags; + u32 reg, val; + + spin_lock_irqsave(&imx_lpcg_scu_lock, flags); + + reg = readl_relaxed(clk->reg); + reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx); + + val = CLK_GATE_SCU_LPCG_SW_SEL; + if (clk->hw_gate) + val |= CLK_GATE_SCU_LPCG_HW_SEL; + + reg |= val << clk->bit_idx; + writel(reg, clk->reg); + + spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); + + return 0; +} + +static void clk_lpcg_scu_disable(struct clk_hw *hw) +{ + struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&imx_lpcg_scu_lock, flags); + + reg = readl_relaxed(clk->reg); + reg &= ~(CLK_GATE_SCU_LPCG_MASK << clk->bit_idx); + writel(reg, clk->reg); + + spin_unlock_irqrestore(&imx_lpcg_scu_lock, flags); +} + +static const struct clk_ops clk_lpcg_scu_ops = { + .enable = clk_lpcg_scu_enable, + .disable = clk_lpcg_scu_disable, +}; + +struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, bool hw_gate) +{ + struct clk_lpcg_scu *clk; + struct clk_init_data init; + struct clk_hw *hw; + int ret; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) + return ERR_PTR(-ENOMEM); + + clk->reg = reg; + clk->bit_idx = bit_idx; + clk->hw_gate = hw_gate; + + init.name = name; + init.ops = &clk_lpcg_scu_ops; + init.flags = CLK_SET_RATE_PARENT | flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + clk->hw.init = &init; + + hw = &clk->hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(clk); + hw = ERR_PTR(ret); + return hw; + } + + if (dev) + dev_set_drvdata(dev, clk); + + return hw; +} + +void imx_clk_lpcg_scu_unregister(struct clk_hw *hw) +{ + struct clk_lpcg_scu *clk = to_clk_lpcg_scu(hw); + + clk_hw_unregister(&clk->hw); + kfree(clk); +} + +static int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev) +{ + struct clk_lpcg_scu *clk = dev_get_drvdata(dev); + + clk->state = readl_relaxed(clk->reg); + dev_dbg(dev, "save lpcg state 0x%x\n", clk->state); + + return 0; +} + +static int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev) +{ + struct clk_lpcg_scu *clk = dev_get_drvdata(dev); + + /* + * FIXME: Sometimes writes don't work unless the CPU issues + * them twice + */ + + writel(clk->state, clk->reg); + writel(clk->state, clk->reg); + dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state); + + return 0; +} + +const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend, + imx_clk_lpcg_scu_resume) +}; diff --git a/drivers/clk/imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c new file mode 100644 index 000000000..5d2a9a3be --- /dev/null +++ b/drivers/clk/imx/clk-pfd.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + */ + +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/err.h> +#include "clk.h" + +/** + * struct clk_pfd - IMX PFD clock + * @hw: clock source + * @reg: PFD register address + * @idx: the index of PFD encoded in the register + * + * PFD clock found on i.MX6 series. Each register for PFD has 4 clk_pfd + * data encoded, and member idx is used to specify the one. And each + * register has SET, CLR and TOG registers at offset 0x4 0x8 and 0xc. + */ +struct clk_pfd { + struct clk_hw hw; + void __iomem *reg; + u8 idx; +}; + +#define to_clk_pfd(_hw) container_of(_hw, struct clk_pfd, hw) + +#define SET 0x4 +#define CLR 0x8 +#define OTG 0xc + +static int clk_pfd_enable(struct clk_hw *hw) +{ + struct clk_pfd *pfd = to_clk_pfd(hw); + + writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + CLR); + + return 0; +} + +static void clk_pfd_disable(struct clk_hw *hw) +{ + struct clk_pfd *pfd = to_clk_pfd(hw); + + writel_relaxed(1 << ((pfd->idx + 1) * 8 - 1), pfd->reg + SET); +} + +static unsigned long clk_pfd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pfd *pfd = to_clk_pfd(hw); + u64 tmp = parent_rate; + u8 frac = (readl_relaxed(pfd->reg) >> (pfd->idx * 8)) & 0x3f; + + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static long clk_pfd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + u64 tmp = *prate; + u8 frac; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + tmp = *prate; + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static int clk_pfd_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pfd *pfd = to_clk_pfd(hw); + u64 tmp = parent_rate; + u8 frac; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + + writel_relaxed(0x3f << (pfd->idx * 8), pfd->reg + CLR); + writel_relaxed(frac << (pfd->idx * 8), pfd->reg + SET); + + return 0; +} + +static int clk_pfd_is_enabled(struct clk_hw *hw) +{ + struct clk_pfd *pfd = to_clk_pfd(hw); + + if (readl_relaxed(pfd->reg) & (1 << ((pfd->idx + 1) * 8 - 1))) + return 0; + + return 1; +} + +static const struct clk_ops clk_pfd_ops = { + .enable = clk_pfd_enable, + .disable = clk_pfd_disable, + .recalc_rate = clk_pfd_recalc_rate, + .round_rate = clk_pfd_round_rate, + .set_rate = clk_pfd_set_rate, + .is_enabled = clk_pfd_is_enabled, +}; + +struct clk_hw *imx_clk_hw_pfd(const char *name, const char *parent_name, + void __iomem *reg, u8 idx) +{ + struct clk_pfd *pfd; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); + if (!pfd) + return ERR_PTR(-ENOMEM); + + pfd->reg = reg; + pfd->idx = idx; + + init.name = name; + init.ops = &clk_pfd_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + pfd->hw.init = &init; + hw = &pfd->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pfd); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-pfdv2.c b/drivers/clk/imx/clk-pfdv2.c new file mode 100644 index 000000000..6ca53a960 --- /dev/null +++ b/drivers/clk/imx/clk-pfdv2.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Author: Dong Aisheng <aisheng.dong@nxp.com> + * + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> + +#include "clk.h" + +/** + * struct clk_pfdv2 - IMX PFD clock + * @hw: clock source + * @reg: PFD register address + * @gate_bit: Gate bit offset + * @vld_bit: Valid bit offset + * @frac_off: PLL Fractional Divider offset + */ + +struct clk_pfdv2 { + struct clk_hw hw; + void __iomem *reg; + u8 gate_bit; + u8 vld_bit; + u8 frac_off; +}; + +#define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw) + +#define CLK_PFDV2_FRAC_MASK 0x3f + +#define LOCK_TIMEOUT_US USEC_PER_MSEC + +static DEFINE_SPINLOCK(pfd_lock); + +static int clk_pfdv2_wait(struct clk_pfdv2 *pfd) +{ + u32 val; + + return readl_poll_timeout(pfd->reg, val, val & (1 << pfd->vld_bit), + 0, LOCK_TIMEOUT_US); +} + +static int clk_pfdv2_enable(struct clk_hw *hw) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pfd_lock, flags); + val = readl_relaxed(pfd->reg); + val &= ~(1 << pfd->gate_bit); + writel_relaxed(val, pfd->reg); + spin_unlock_irqrestore(&pfd_lock, flags); + + return clk_pfdv2_wait(pfd); +} + +static void clk_pfdv2_disable(struct clk_hw *hw) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pfd_lock, flags); + val = readl_relaxed(pfd->reg); + val |= (1 << pfd->gate_bit); + writel_relaxed(val, pfd->reg); + spin_unlock_irqrestore(&pfd_lock, flags); +} + +static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + u64 tmp = parent_rate; + u8 frac; + + frac = (readl_relaxed(pfd->reg) >> pfd->frac_off) + & CLK_PFDV2_FRAC_MASK; + + if (!frac) { + pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n", + clk_hw_get_name(hw)); + return 0; + } + + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static int clk_pfdv2_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long parent_rates[] = { + 480000000, + 528000000, + req->best_parent_rate + }; + unsigned long best_rate = -1UL, rate = req->rate; + unsigned long best_parent_rate = req->best_parent_rate; + u64 tmp; + u8 frac; + int i; + + for (i = 0; i < ARRAY_SIZE(parent_rates); i++) { + tmp = parent_rates[i]; + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + + tmp = parent_rates[i]; + tmp *= 18; + do_div(tmp, frac); + + if (abs(tmp - req->rate) < abs(best_rate - req->rate)) { + best_rate = tmp; + best_parent_rate = parent_rates[i]; + } + } + + req->best_parent_rate = best_parent_rate; + req->rate = best_rate; + + return 0; +} + +static int clk_pfdv2_is_enabled(struct clk_hw *hw) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + + if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit)) + return 0; + + return 1; +} + +static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + unsigned long flags; + u64 tmp = parent_rate; + u32 val; + u8 frac; + + if (!rate) + return -EINVAL; + + /* + * PFD can NOT change rate without gating. + * as the PFDs may enabled in HW by default but no + * consumer used it, the enable count is '0', so the + * 'SET_RATE_GATE' can NOT help on blocking the set_rate + * ops especially for 'assigned-clock-xxx'. In order + * to simplify the case, just disable the PFD if it is + * enabled in HW but not in SW. + */ + if (clk_pfdv2_is_enabled(hw)) + clk_pfdv2_disable(hw); + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + + spin_lock_irqsave(&pfd_lock, flags); + val = readl_relaxed(pfd->reg); + val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); + val |= frac << pfd->frac_off; + writel_relaxed(val, pfd->reg); + spin_unlock_irqrestore(&pfd_lock, flags); + + return 0; +} + +static const struct clk_ops clk_pfdv2_ops = { + .enable = clk_pfdv2_enable, + .disable = clk_pfdv2_disable, + .recalc_rate = clk_pfdv2_recalc_rate, + .determine_rate = clk_pfdv2_determine_rate, + .set_rate = clk_pfdv2_set_rate, + .is_enabled = clk_pfdv2_is_enabled, +}; + +struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name, + const char *parent_name, void __iomem *reg, u8 idx) +{ + struct clk_init_data init; + struct clk_pfdv2 *pfd; + struct clk_hw *hw; + int ret; + + WARN_ON(idx > 3); + + pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); + if (!pfd) + return ERR_PTR(-ENOMEM); + + pfd->reg = reg; + pfd->gate_bit = (idx + 1) * 8 - 1; + pfd->vld_bit = pfd->gate_bit - 1; + pfd->frac_off = idx * 8; + + init.name = name; + init.ops = &clk_pfdv2_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + if (type == IMX_PFDV2_IMX7ULP) + init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; + else + init.flags = CLK_SET_RATE_GATE; + + pfd->hw.init = &init; + + hw = &pfd->hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pfd); + hw = ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx_clk_hw_pfdv2); diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c new file mode 100644 index 000000000..416d582a9 --- /dev/null +++ b/drivers/clk/imx/clk-pll14xx.c @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2017-2018 NXP. + */ + +#define pr_fmt(fmt) "pll14xx: " fmt + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> +#include <linux/jiffies.h> + +#include "clk.h" + +#define GNRL_CTL 0x0 +#define DIV_CTL0 0x4 +#define DIV_CTL1 0x8 +#define LOCK_STATUS BIT(31) +#define LOCK_SEL_MASK BIT(29) +#define CLKE_MASK BIT(11) +#define RST_MASK BIT(9) +#define BYPASS_MASK BIT(4) +#define MDIV_MASK GENMASK(21, 12) +#define PDIV_MASK GENMASK(9, 4) +#define SDIV_MASK GENMASK(2, 0) +#define KDIV_MASK GENMASK(15, 0) +#define KDIV_MIN SHRT_MIN +#define KDIV_MAX SHRT_MAX + +#define LOCK_TIMEOUT_US 10000 + +struct clk_pll14xx { + struct clk_hw hw; + void __iomem *base; + enum imx_pll14xx_type type; + const struct imx_pll14xx_rate_table *rate_table; + int rate_count; +}; + +#define to_clk_pll14xx(_hw) container_of(_hw, struct clk_pll14xx, hw) + +static const struct imx_pll14xx_rate_table imx_pll1416x_tbl[] = { + PLL_1416X_RATE(1800000000U, 225, 3, 0), + PLL_1416X_RATE(1600000000U, 200, 3, 0), + PLL_1416X_RATE(1500000000U, 375, 3, 1), + PLL_1416X_RATE(1400000000U, 350, 3, 1), + PLL_1416X_RATE(1200000000U, 300, 3, 1), + PLL_1416X_RATE(1000000000U, 250, 3, 1), + PLL_1416X_RATE(800000000U, 200, 3, 1), + PLL_1416X_RATE(750000000U, 250, 2, 2), + PLL_1416X_RATE(700000000U, 350, 3, 2), + PLL_1416X_RATE(600000000U, 300, 3, 2), +}; + +static const struct imx_pll14xx_rate_table imx_pll1443x_tbl[] = { + PLL_1443X_RATE(1039500000U, 173, 2, 1, 16384), + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), + PLL_1443X_RATE(594000000U, 198, 2, 2, 0), + PLL_1443X_RATE(519750000U, 173, 2, 2, 16384), +}; + +struct imx_pll14xx_clk imx_1443x_pll = { + .type = PLL_1443X, + .rate_table = imx_pll1443x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1443x_tbl), +}; +EXPORT_SYMBOL_GPL(imx_1443x_pll); + +struct imx_pll14xx_clk imx_1443x_dram_pll = { + .type = PLL_1443X, + .rate_table = imx_pll1443x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1443x_tbl), + .flags = CLK_GET_RATE_NOCACHE, +}; +EXPORT_SYMBOL_GPL(imx_1443x_dram_pll); + +struct imx_pll14xx_clk imx_1416x_pll = { + .type = PLL_1416X, + .rate_table = imx_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1416x_tbl), +}; +EXPORT_SYMBOL_GPL(imx_1416x_pll); + +static const struct imx_pll14xx_rate_table *imx_get_pll_settings( + struct clk_pll14xx *pll, unsigned long rate) +{ + const struct imx_pll14xx_rate_table *rate_table = pll->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) + if (rate == rate_table[i].rate) + return &rate_table[i]; + + return NULL; +} + +static long pll14xx_calc_rate(struct clk_pll14xx *pll, int mdiv, int pdiv, + int sdiv, int kdiv, unsigned long prate) +{ + u64 fvco = prate; + + /* fvco = (m * 65536 + k) * Fin / (p * 65536) */ + fvco *= (mdiv * 65536 + kdiv); + pdiv *= 65536; + + do_div(fvco, pdiv << sdiv); + + return fvco; +} + +static long pll1443x_calc_kdiv(int mdiv, int pdiv, int sdiv, + unsigned long rate, unsigned long prate) +{ + long kdiv; + + /* calc kdiv = round(rate * pdiv * 65536 * 2^sdiv / prate) - (mdiv * 65536) */ + kdiv = ((rate * ((pdiv * 65536) << sdiv) + prate / 2) / prate) - (mdiv * 65536); + + return clamp_t(short, kdiv, KDIV_MIN, KDIV_MAX); +} + +static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rate, + unsigned long prate, struct imx_pll14xx_rate_table *t) +{ + u32 pll_div_ctl0, pll_div_ctl1; + int mdiv, pdiv, sdiv, kdiv; + long fvco, rate_min, rate_max, dist, best = LONG_MAX; + const struct imx_pll14xx_rate_table *tt; + + /* + * Fractional PLL constrains: + * + * a) 1 <= p <= 63 + * b) 64 <= m <= 1023 + * c) 0 <= s <= 6 + * d) -32768 <= k <= 32767 + * + * fvco = (m * 65536 + k) * prate / (p * 65536) + */ + + /* First try if we can get the desired rate from one of the static entries */ + tt = imx_get_pll_settings(pll, rate); + if (tt) { + pr_debug("%s: in=%ld, want=%ld, Using PLL setting from table\n", + clk_hw_get_name(&pll->hw), prate, rate); + t->rate = tt->rate; + t->mdiv = tt->mdiv; + t->pdiv = tt->pdiv; + t->sdiv = tt->sdiv; + t->kdiv = tt->kdiv; + return; + } + + pll_div_ctl0 = readl_relaxed(pll->base + DIV_CTL0); + mdiv = FIELD_GET(MDIV_MASK, pll_div_ctl0); + pdiv = FIELD_GET(PDIV_MASK, pll_div_ctl0); + sdiv = FIELD_GET(SDIV_MASK, pll_div_ctl0); + pll_div_ctl1 = readl_relaxed(pll->base + DIV_CTL1); + + /* Then see if we can get the desired rate by only adjusting kdiv (glitch free) */ + rate_min = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, KDIV_MIN, prate); + rate_max = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, KDIV_MAX, prate); + + if (rate >= rate_min && rate <= rate_max) { + kdiv = pll1443x_calc_kdiv(mdiv, pdiv, sdiv, rate, prate); + pr_debug("%s: in=%ld, want=%ld Only adjust kdiv %ld -> %d\n", + clk_hw_get_name(&pll->hw), prate, rate, + FIELD_GET(KDIV_MASK, pll_div_ctl1), kdiv); + fvco = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate); + t->rate = (unsigned int)fvco; + t->mdiv = mdiv; + t->pdiv = pdiv; + t->sdiv = sdiv; + t->kdiv = kdiv; + return; + } + + /* Finally calculate best values */ + for (pdiv = 1; pdiv <= 63; pdiv++) { + for (sdiv = 0; sdiv <= 6; sdiv++) { + /* calc mdiv = round(rate * pdiv * 2^sdiv) / prate) */ + mdiv = DIV_ROUND_CLOSEST(rate * (pdiv << sdiv), prate); + mdiv = clamp(mdiv, 64, 1023); + + kdiv = pll1443x_calc_kdiv(mdiv, pdiv, sdiv, rate, prate); + fvco = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate); + + /* best match */ + dist = abs((long)rate - (long)fvco); + if (dist < best) { + best = dist; + t->rate = (unsigned int)fvco; + t->mdiv = mdiv; + t->pdiv = pdiv; + t->sdiv = sdiv; + t->kdiv = kdiv; + + if (!dist) + goto found; + } + } + } +found: + pr_debug("%s: in=%ld, want=%ld got=%d (pdiv=%d sdiv=%d mdiv=%d kdiv=%d)\n", + clk_hw_get_name(&pll->hw), prate, rate, t->rate, t->pdiv, t->sdiv, + t->mdiv, t->kdiv); +} + +static long clk_pll1416x_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + const struct imx_pll14xx_rate_table *rate_table = pll->rate_table; + int i; + + /* Assuming rate_table is in descending order */ + for (i = 0; i < pll->rate_count; i++) + if (rate >= rate_table[i].rate) + return rate_table[i].rate; + + /* return minimum supported value */ + return rate_table[pll->rate_count - 1].rate; +} + +static long clk_pll1443x_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + struct imx_pll14xx_rate_table t; + + imx_pll14xx_calc_settings(pll, rate, *prate, &t); + + return t.rate; +} + +static unsigned long clk_pll14xx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 mdiv, pdiv, sdiv, kdiv, pll_div_ctl0, pll_div_ctl1; + + pll_div_ctl0 = readl_relaxed(pll->base + DIV_CTL0); + mdiv = FIELD_GET(MDIV_MASK, pll_div_ctl0); + pdiv = FIELD_GET(PDIV_MASK, pll_div_ctl0); + sdiv = FIELD_GET(SDIV_MASK, pll_div_ctl0); + + if (pll->type == PLL_1443X) { + pll_div_ctl1 = readl_relaxed(pll->base + DIV_CTL1); + kdiv = FIELD_GET(KDIV_MASK, pll_div_ctl1); + } else { + kdiv = 0; + } + + return pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, parent_rate); +} + +static inline bool clk_pll14xx_mp_change(const struct imx_pll14xx_rate_table *rate, + u32 pll_div) +{ + u32 old_mdiv, old_pdiv; + + old_mdiv = FIELD_GET(MDIV_MASK, pll_div); + old_pdiv = FIELD_GET(PDIV_MASK, pll_div); + + return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv; +} + +static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll) +{ + u32 val; + + return readl_poll_timeout(pll->base + GNRL_CTL, val, val & LOCK_STATUS, 0, + LOCK_TIMEOUT_US); +} + +static int clk_pll1416x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + const struct imx_pll14xx_rate_table *rate; + u32 tmp, div_val; + int ret; + + rate = imx_get_pll_settings(pll, drate); + if (!rate) { + pr_err("Invalid rate %lu for pll clk %s\n", drate, + clk_hw_get_name(hw)); + return -EINVAL; + } + + tmp = readl_relaxed(pll->base + DIV_CTL0); + + if (!clk_pll14xx_mp_change(rate, tmp)) { + tmp &= ~SDIV_MASK; + tmp |= FIELD_PREP(SDIV_MASK, rate->sdiv); + writel_relaxed(tmp, pll->base + DIV_CTL0); + + return 0; + } + + /* Bypass clock and set lock to pll output lock */ + tmp = readl_relaxed(pll->base + GNRL_CTL); + tmp |= LOCK_SEL_MASK; + writel_relaxed(tmp, pll->base + GNRL_CTL); + + /* Enable RST */ + tmp &= ~RST_MASK; + writel_relaxed(tmp, pll->base + GNRL_CTL); + + /* Enable BYPASS */ + tmp |= BYPASS_MASK; + writel(tmp, pll->base + GNRL_CTL); + + div_val = FIELD_PREP(MDIV_MASK, rate->mdiv) | FIELD_PREP(PDIV_MASK, rate->pdiv) | + FIELD_PREP(SDIV_MASK, rate->sdiv); + writel_relaxed(div_val, pll->base + DIV_CTL0); + + /* + * According to SPEC, t3 - t2 need to be greater than + * 1us and 1/FREF, respectively. + * FREF is FIN / Prediv, the prediv is [1, 63], so choose + * 3us. + */ + udelay(3); + + /* Disable RST */ + tmp |= RST_MASK; + writel_relaxed(tmp, pll->base + GNRL_CTL); + + /* Wait Lock */ + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + /* Bypass */ + tmp &= ~BYPASS_MASK; + writel_relaxed(tmp, pll->base + GNRL_CTL); + + return 0; +} + +static int clk_pll1443x_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + struct imx_pll14xx_rate_table rate; + u32 gnrl_ctl, div_ctl0; + int ret; + + imx_pll14xx_calc_settings(pll, drate, prate, &rate); + + div_ctl0 = readl_relaxed(pll->base + DIV_CTL0); + + if (!clk_pll14xx_mp_change(&rate, div_ctl0)) { + /* only sdiv and/or kdiv changed - no need to RESET PLL */ + div_ctl0 &= ~SDIV_MASK; + div_ctl0 |= FIELD_PREP(SDIV_MASK, rate.sdiv); + writel_relaxed(div_ctl0, pll->base + DIV_CTL0); + + writel_relaxed(FIELD_PREP(KDIV_MASK, rate.kdiv), + pll->base + DIV_CTL1); + + return 0; + } + + /* Enable RST */ + gnrl_ctl = readl_relaxed(pll->base + GNRL_CTL); + gnrl_ctl &= ~RST_MASK; + writel_relaxed(gnrl_ctl, pll->base + GNRL_CTL); + + /* Enable BYPASS */ + gnrl_ctl |= BYPASS_MASK; + writel_relaxed(gnrl_ctl, pll->base + GNRL_CTL); + + div_ctl0 = FIELD_PREP(MDIV_MASK, rate.mdiv) | + FIELD_PREP(PDIV_MASK, rate.pdiv) | + FIELD_PREP(SDIV_MASK, rate.sdiv); + writel_relaxed(div_ctl0, pll->base + DIV_CTL0); + + writel_relaxed(FIELD_PREP(KDIV_MASK, rate.kdiv), pll->base + DIV_CTL1); + + /* + * According to SPEC, t3 - t2 need to be greater than + * 1us and 1/FREF, respectively. + * FREF is FIN / Prediv, the prediv is [1, 63], so choose + * 3us. + */ + udelay(3); + + /* Disable RST */ + gnrl_ctl |= RST_MASK; + writel_relaxed(gnrl_ctl, pll->base + GNRL_CTL); + + /* Wait Lock*/ + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + /* Bypass */ + gnrl_ctl &= ~BYPASS_MASK; + writel_relaxed(gnrl_ctl, pll->base + GNRL_CTL); + + return 0; +} + +static int clk_pll14xx_prepare(struct clk_hw *hw) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 val; + int ret; + + /* + * RESETB = 1 from 0, PLL starts its normal + * operation after lock time + */ + val = readl_relaxed(pll->base + GNRL_CTL); + if (val & RST_MASK) + return 0; + val |= BYPASS_MASK; + writel_relaxed(val, pll->base + GNRL_CTL); + val |= RST_MASK; + writel_relaxed(val, pll->base + GNRL_CTL); + + ret = clk_pll14xx_wait_lock(pll); + if (ret) + return ret; + + val &= ~BYPASS_MASK; + writel_relaxed(val, pll->base + GNRL_CTL); + + return 0; +} + +static int clk_pll14xx_is_prepared(struct clk_hw *hw) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 val; + + val = readl_relaxed(pll->base + GNRL_CTL); + + return (val & RST_MASK) ? 1 : 0; +} + +static void clk_pll14xx_unprepare(struct clk_hw *hw) +{ + struct clk_pll14xx *pll = to_clk_pll14xx(hw); + u32 val; + + /* + * Set RST to 0, power down mode is enabled and + * every digital block is reset + */ + val = readl_relaxed(pll->base + GNRL_CTL); + val &= ~RST_MASK; + writel_relaxed(val, pll->base + GNRL_CTL); +} + +static const struct clk_ops clk_pll1416x_ops = { + .prepare = clk_pll14xx_prepare, + .unprepare = clk_pll14xx_unprepare, + .is_prepared = clk_pll14xx_is_prepared, + .recalc_rate = clk_pll14xx_recalc_rate, + .round_rate = clk_pll1416x_round_rate, + .set_rate = clk_pll1416x_set_rate, +}; + +static const struct clk_ops clk_pll1416x_min_ops = { + .recalc_rate = clk_pll14xx_recalc_rate, +}; + +static const struct clk_ops clk_pll1443x_ops = { + .prepare = clk_pll14xx_prepare, + .unprepare = clk_pll14xx_unprepare, + .is_prepared = clk_pll14xx_is_prepared, + .recalc_rate = clk_pll14xx_recalc_rate, + .round_rate = clk_pll1443x_round_rate, + .set_rate = clk_pll1443x_set_rate, +}; + +struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, + const char *parent_name, void __iomem *base, + const struct imx_pll14xx_clk *pll_clk) +{ + struct clk_pll14xx *pll; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + u32 val; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = pll_clk->flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + switch (pll_clk->type) { + case PLL_1416X: + if (!pll_clk->rate_table) + init.ops = &clk_pll1416x_min_ops; + else + init.ops = &clk_pll1416x_ops; + break; + case PLL_1443X: + init.ops = &clk_pll1443x_ops; + break; + default: + pr_err("Unknown pll type for pll clk %s\n", name); + kfree(pll); + return ERR_PTR(-EINVAL); + } + + pll->base = base; + pll->hw.init = &init; + pll->type = pll_clk->type; + pll->rate_table = pll_clk->rate_table; + pll->rate_count = pll_clk->rate_count; + + val = readl_relaxed(pll->base + GNRL_CTL); + val &= ~BYPASS_MASK; + writel_relaxed(val, pll->base + GNRL_CTL); + + hw = &pll->hw; + + ret = clk_hw_register(dev, hw); + if (ret) { + pr_err("failed to register pll %s %d\n", name, ret); + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx_dev_clk_hw_pll14xx); diff --git a/drivers/clk/imx/clk-pllv1.c b/drivers/clk/imx/clk-pllv1.c new file mode 100644 index 000000000..93ee81b28 --- /dev/null +++ b/drivers/clk/imx/clk-pllv1.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/err.h> + +#include "clk.h" + +#define MFN_BITS (10) +#define MFN_SIGN (BIT(MFN_BITS - 1)) +#define MFN_MASK (MFN_SIGN - 1) + +/** + * struct clk_pllv1 - IMX PLLv1 clock descriptor + * + * @hw: clock source + * @base: base address of pll registers + * @type: type of IMX_PLLV1 + * + * PLL clock version 1, found on i.MX1/21/25/27/31/35 + */ +struct clk_pllv1 { + struct clk_hw hw; + void __iomem *base; + enum imx_pllv1_type type; +}; + +#define to_clk_pllv1(clk) (container_of(clk, struct clk_pllv1, clk)) + +static inline bool is_imx1_pllv1(struct clk_pllv1 *pll) +{ + return pll->type == IMX_PLLV1_IMX1; +} + +static inline bool is_imx21_pllv1(struct clk_pllv1 *pll) +{ + return pll->type == IMX_PLLV1_IMX21; +} + +static inline bool is_imx27_pllv1(struct clk_pllv1 *pll) +{ + return pll->type == IMX_PLLV1_IMX27; +} + +static inline bool mfn_is_negative(struct clk_pllv1 *pll, unsigned int mfn) +{ + return !is_imx1_pllv1(pll) && !is_imx21_pllv1(pll) && (mfn & MFN_SIGN); +} + +static unsigned long clk_pllv1_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv1 *pll = to_clk_pllv1(hw); + unsigned long long ull; + int mfn_abs; + unsigned int mfi, mfn, mfd, pd; + u32 reg; + unsigned long rate; + + reg = readl(pll->base); + + /* + * Get the resulting clock rate from a PLL register value and the input + * frequency. PLLs with this register layout can be found on i.MX1, + * i.MX21, i.MX27 and i,MX31 + * + * mfi + mfn / (mfd + 1) + * f = 2 * f_ref * -------------------- + * pd + 1 + */ + + mfi = (reg >> 10) & 0xf; + mfn = reg & 0x3ff; + mfd = (reg >> 16) & 0x3ff; + pd = (reg >> 26) & 0xf; + + mfi = mfi <= 5 ? 5 : mfi; + + mfn_abs = mfn; + + /* + * On all i.MXs except i.MX1 and i.MX21 mfn is a 10bit + * 2's complements number. + * On i.MX27 the bit 9 is the sign bit. + */ + if (mfn_is_negative(pll, mfn)) { + if (is_imx27_pllv1(pll)) + mfn_abs = mfn & MFN_MASK; + else + mfn_abs = BIT(MFN_BITS) - mfn; + } + + rate = parent_rate * 2; + rate /= pd + 1; + + ull = (unsigned long long)rate * mfn_abs; + + do_div(ull, mfd + 1); + + if (mfn_is_negative(pll, mfn)) + ull = (rate * mfi) - ull; + else + ull = (rate * mfi) + ull; + + return ull; +} + +static const struct clk_ops clk_pllv1_ops = { + .recalc_rate = clk_pllv1_recalc_rate, +}; + +struct clk_hw *imx_clk_hw_pllv1(enum imx_pllv1_type type, const char *name, + const char *parent, void __iomem *base) +{ + struct clk_pllv1 *pll; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + pll = kmalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + pll->type = type; + + init.name = name; + init.ops = &clk_pllv1_ops; + init.flags = 0; + init.parent_names = &parent; + init.num_parents = 1; + + pll->hw.init = &init; + hw = &pll->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-pllv2.c b/drivers/clk/imx/clk-pllv2.c new file mode 100644 index 000000000..ff17f0664 --- /dev/null +++ b/drivers/clk/imx/clk-pllv2.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/err.h> + +#include <asm/div64.h> + +#include "clk.h" + +#define to_clk_pllv2(clk) (container_of(clk, struct clk_pllv2, clk)) + +/* PLL Register Offsets */ +#define MXC_PLL_DP_CTL 0x00 +#define MXC_PLL_DP_CONFIG 0x04 +#define MXC_PLL_DP_OP 0x08 +#define MXC_PLL_DP_MFD 0x0C +#define MXC_PLL_DP_MFN 0x10 +#define MXC_PLL_DP_MFNMINUS 0x14 +#define MXC_PLL_DP_MFNPLUS 0x18 +#define MXC_PLL_DP_HFS_OP 0x1C +#define MXC_PLL_DP_HFS_MFD 0x20 +#define MXC_PLL_DP_HFS_MFN 0x24 +#define MXC_PLL_DP_MFN_TOGC 0x28 +#define MXC_PLL_DP_DESTAT 0x2c + +/* PLL Register Bit definitions */ +#define MXC_PLL_DP_CTL_MUL_CTRL 0x2000 +#define MXC_PLL_DP_CTL_DPDCK0_2_EN 0x1000 +#define MXC_PLL_DP_CTL_DPDCK0_2_OFFSET 12 +#define MXC_PLL_DP_CTL_ADE 0x800 +#define MXC_PLL_DP_CTL_REF_CLK_DIV 0x400 +#define MXC_PLL_DP_CTL_REF_CLK_SEL_MASK (3 << 8) +#define MXC_PLL_DP_CTL_REF_CLK_SEL_OFFSET 8 +#define MXC_PLL_DP_CTL_HFSM 0x80 +#define MXC_PLL_DP_CTL_PRE 0x40 +#define MXC_PLL_DP_CTL_UPEN 0x20 +#define MXC_PLL_DP_CTL_RST 0x10 +#define MXC_PLL_DP_CTL_RCP 0x8 +#define MXC_PLL_DP_CTL_PLM 0x4 +#define MXC_PLL_DP_CTL_BRM0 0x2 +#define MXC_PLL_DP_CTL_LRF 0x1 + +#define MXC_PLL_DP_CONFIG_BIST 0x8 +#define MXC_PLL_DP_CONFIG_SJC_CE 0x4 +#define MXC_PLL_DP_CONFIG_AREN 0x2 +#define MXC_PLL_DP_CONFIG_LDREQ 0x1 + +#define MXC_PLL_DP_OP_MFI_OFFSET 4 +#define MXC_PLL_DP_OP_MFI_MASK (0xF << 4) +#define MXC_PLL_DP_OP_PDF_OFFSET 0 +#define MXC_PLL_DP_OP_PDF_MASK 0xF + +#define MXC_PLL_DP_MFD_OFFSET 0 +#define MXC_PLL_DP_MFD_MASK 0x07FFFFFF + +#define MXC_PLL_DP_MFN_OFFSET 0x0 +#define MXC_PLL_DP_MFN_MASK 0x07FFFFFF + +#define MXC_PLL_DP_MFN_TOGC_TOG_DIS (1 << 17) +#define MXC_PLL_DP_MFN_TOGC_TOG_EN (1 << 16) +#define MXC_PLL_DP_MFN_TOGC_CNT_OFFSET 0x0 +#define MXC_PLL_DP_MFN_TOGC_CNT_MASK 0xFFFF + +#define MXC_PLL_DP_DESTAT_TOG_SEL (1 << 31) +#define MXC_PLL_DP_DESTAT_MFN 0x07FFFFFF + +#define MAX_DPLL_WAIT_TRIES 1000 /* 1000 * udelay(1) = 1ms */ + +struct clk_pllv2 { + struct clk_hw hw; + void __iomem *base; +}; + +static unsigned long __clk_pllv2_recalc_rate(unsigned long parent_rate, + u32 dp_ctl, u32 dp_op, u32 dp_mfd, u32 dp_mfn) +{ + long mfi, mfn, mfd, pdf, ref_clk; + unsigned long dbl; + u64 temp; + + dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN; + + pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK; + mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET; + mfi = (mfi <= 5) ? 5 : mfi; + mfd = dp_mfd & MXC_PLL_DP_MFD_MASK; + mfn = dp_mfn & MXC_PLL_DP_MFN_MASK; + mfn = sign_extend32(mfn, 26); + + ref_clk = 2 * parent_rate; + if (dbl != 0) + ref_clk *= 2; + + ref_clk /= (pdf + 1); + temp = (u64) ref_clk * abs(mfn); + do_div(temp, mfd + 1); + if (mfn < 0) + temp = (ref_clk * mfi) - temp; + else + temp = (ref_clk * mfi) + temp; + + return temp; +} + +static unsigned long clk_pllv2_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 dp_op, dp_mfd, dp_mfn, dp_ctl; + void __iomem *pllbase; + struct clk_pllv2 *pll = to_clk_pllv2(hw); + + pllbase = pll->base; + + dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); + dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP); + dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD); + dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN); + + return __clk_pllv2_recalc_rate(parent_rate, dp_ctl, dp_op, dp_mfd, dp_mfn); +} + +static int __clk_pllv2_set_rate(unsigned long rate, unsigned long parent_rate, + u32 *dp_op, u32 *dp_mfd, u32 *dp_mfn) +{ + u32 reg; + long mfi, pdf, mfn, mfd = 999999; + u64 temp64; + unsigned long quad_parent_rate; + + quad_parent_rate = 4 * parent_rate; + pdf = mfi = -1; + while (++pdf < 16 && mfi < 5) + mfi = rate * (pdf+1) / quad_parent_rate; + if (mfi > 15) + return -EINVAL; + pdf--; + + temp64 = rate * (pdf + 1) - quad_parent_rate * mfi; + do_div(temp64, quad_parent_rate / 1000000); + mfn = (long)temp64; + + reg = mfi << 4 | pdf; + + *dp_op = reg; + *dp_mfd = mfd; + *dp_mfn = mfn; + + return 0; +} + +static int clk_pllv2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv2 *pll = to_clk_pllv2(hw); + void __iomem *pllbase; + u32 dp_ctl, dp_op, dp_mfd, dp_mfn; + int ret; + + pllbase = pll->base; + + + ret = __clk_pllv2_set_rate(rate, parent_rate, &dp_op, &dp_mfd, &dp_mfn); + if (ret) + return ret; + + dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); + /* use dpdck0_2 */ + __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL); + + __raw_writel(dp_op, pllbase + MXC_PLL_DP_OP); + __raw_writel(dp_mfd, pllbase + MXC_PLL_DP_MFD); + __raw_writel(dp_mfn, pllbase + MXC_PLL_DP_MFN); + + return 0; +} + +static long clk_pllv2_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + u32 dp_op, dp_mfd, dp_mfn; + int ret; + + ret = __clk_pllv2_set_rate(rate, *prate, &dp_op, &dp_mfd, &dp_mfn); + if (ret) + return ret; + + return __clk_pllv2_recalc_rate(*prate, MXC_PLL_DP_CTL_DPDCK0_2_EN, + dp_op, dp_mfd, dp_mfn); +} + +static int clk_pllv2_prepare(struct clk_hw *hw) +{ + struct clk_pllv2 *pll = to_clk_pllv2(hw); + u32 reg; + void __iomem *pllbase; + int i = 0; + + pllbase = pll->base; + reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) | MXC_PLL_DP_CTL_UPEN; + __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); + + /* Wait for lock */ + do { + reg = __raw_readl(pllbase + MXC_PLL_DP_CTL); + if (reg & MXC_PLL_DP_CTL_LRF) + break; + + udelay(1); + } while (++i < MAX_DPLL_WAIT_TRIES); + + if (i == MAX_DPLL_WAIT_TRIES) { + pr_err("MX5: pll locking failed\n"); + return -EINVAL; + } + + return 0; +} + +static void clk_pllv2_unprepare(struct clk_hw *hw) +{ + struct clk_pllv2 *pll = to_clk_pllv2(hw); + u32 reg; + void __iomem *pllbase; + + pllbase = pll->base; + reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN; + __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); +} + +static const struct clk_ops clk_pllv2_ops = { + .prepare = clk_pllv2_prepare, + .unprepare = clk_pllv2_unprepare, + .recalc_rate = clk_pllv2_recalc_rate, + .round_rate = clk_pllv2_round_rate, + .set_rate = clk_pllv2_set_rate, +}; + +struct clk_hw *imx_clk_hw_pllv2(const char *name, const char *parent, + void __iomem *base) +{ + struct clk_pllv2 *pll; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + + init.name = name; + init.ops = &clk_pllv2_ops; + init.flags = 0; + init.parent_names = &parent; + init.num_parents = 1; + + pll->hw.init = &init; + hw = &pll->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c new file mode 100644 index 000000000..eea32f87c --- /dev/null +++ b/drivers/clk/imx/clk-pllv3.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/err.h> +#include "clk.h" + +#define PLL_NUM_OFFSET 0x10 +#define PLL_DENOM_OFFSET 0x20 +#define PLL_IMX7_NUM_OFFSET 0x20 +#define PLL_IMX7_DENOM_OFFSET 0x30 + +#define PLL_VF610_NUM_OFFSET 0x20 +#define PLL_VF610_DENOM_OFFSET 0x30 + +#define BM_PLL_POWER (0x1 << 12) +#define BM_PLL_LOCK (0x1 << 31) +#define IMX7_ENET_PLL_POWER (0x1 << 5) +#define IMX7_DDR_PLL_POWER (0x1 << 20) + +#define PLL_LOCK_TIMEOUT 10000 + +/** + * struct clk_pllv3 - IMX PLL clock version 3 + * @hw: clock source + * @base: base address of PLL registers + * @power_bit: pll power bit mask + * @powerup_set: set power_bit to power up the PLL + * @div_mask: mask of divider bits + * @div_shift: shift of divider bits + * @ref_clock: reference clock rate + * @num_offset: num register offset + * @denom_offset: denom register offset + * + * IMX PLL clock version 3, found on i.MX6 series. Divider for pllv3 + * is actually a multiplier, and always sits at bit 0. + */ +struct clk_pllv3 { + struct clk_hw hw; + void __iomem *base; + u32 power_bit; + bool powerup_set; + u32 div_mask; + u32 div_shift; + unsigned long ref_clock; + u32 num_offset; + u32 denom_offset; +}; + +#define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw) + +static int clk_pllv3_wait_lock(struct clk_pllv3 *pll) +{ + u32 val = readl_relaxed(pll->base) & pll->power_bit; + + /* No need to wait for lock when pll is not powered up */ + if ((pll->powerup_set && !val) || (!pll->powerup_set && val)) + return 0; + + return readl_relaxed_poll_timeout(pll->base, val, val & BM_PLL_LOCK, + 500, PLL_LOCK_TIMEOUT); +} + +static int clk_pllv3_prepare(struct clk_hw *hw) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 val; + + val = readl_relaxed(pll->base); + if (pll->powerup_set) + val |= pll->power_bit; + else + val &= ~pll->power_bit; + writel_relaxed(val, pll->base); + + return clk_pllv3_wait_lock(pll); +} + +static void clk_pllv3_unprepare(struct clk_hw *hw) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 val; + + val = readl_relaxed(pll->base); + if (pll->powerup_set) + val &= ~pll->power_bit; + else + val |= pll->power_bit; + writel_relaxed(val, pll->base); +} + +static int clk_pllv3_is_prepared(struct clk_hw *hw) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + + if (readl_relaxed(pll->base) & BM_PLL_LOCK) + return 1; + + return 0; +} + +static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 div = (readl_relaxed(pll->base) >> pll->div_shift) & pll->div_mask; + + return (div == 1) ? parent_rate * 22 : parent_rate * 20; +} + +static long clk_pllv3_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + + return (rate >= parent_rate * 22) ? parent_rate * 22 : + parent_rate * 20; +} + +static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 val, div; + + if (rate == parent_rate * 22) + div = 1; + else if (rate == parent_rate * 20) + div = 0; + else + return -EINVAL; + + val = readl_relaxed(pll->base); + val &= ~(pll->div_mask << pll->div_shift); + val |= (div << pll->div_shift); + writel_relaxed(val, pll->base); + + return clk_pllv3_wait_lock(pll); +} + +static const struct clk_ops clk_pllv3_ops = { + .prepare = clk_pllv3_prepare, + .unprepare = clk_pllv3_unprepare, + .is_prepared = clk_pllv3_is_prepared, + .recalc_rate = clk_pllv3_recalc_rate, + .round_rate = clk_pllv3_round_rate, + .set_rate = clk_pllv3_set_rate, +}; + +static unsigned long clk_pllv3_sys_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 div = readl_relaxed(pll->base) & pll->div_mask; + + return parent_rate * div / 2; +} + +static long clk_pllv3_sys_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + unsigned long min_rate = parent_rate * 54 / 2; + unsigned long max_rate = parent_rate * 108 / 2; + u32 div; + + if (rate > max_rate) + rate = max_rate; + else if (rate < min_rate) + rate = min_rate; + div = rate * 2 / parent_rate; + + return parent_rate * div / 2; +} + +static int clk_pllv3_sys_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + unsigned long min_rate = parent_rate * 54 / 2; + unsigned long max_rate = parent_rate * 108 / 2; + u32 val, div; + + if (rate < min_rate || rate > max_rate) + return -EINVAL; + + div = rate * 2 / parent_rate; + val = readl_relaxed(pll->base); + val &= ~pll->div_mask; + val |= div; + writel_relaxed(val, pll->base); + + return clk_pllv3_wait_lock(pll); +} + +static const struct clk_ops clk_pllv3_sys_ops = { + .prepare = clk_pllv3_prepare, + .unprepare = clk_pllv3_unprepare, + .is_prepared = clk_pllv3_is_prepared, + .recalc_rate = clk_pllv3_sys_recalc_rate, + .round_rate = clk_pllv3_sys_round_rate, + .set_rate = clk_pllv3_sys_set_rate, +}; + +static unsigned long clk_pllv3_av_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + u32 mfn = readl_relaxed(pll->base + pll->num_offset); + u32 mfd = readl_relaxed(pll->base + pll->denom_offset); + u32 div = readl_relaxed(pll->base) & pll->div_mask; + u64 temp64 = (u64)parent_rate; + + temp64 *= mfn; + do_div(temp64, mfd); + + return parent_rate * div + (unsigned long)temp64; +} + +static long clk_pllv3_av_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + unsigned long min_rate = parent_rate * 27; + unsigned long max_rate = parent_rate * 54; + u32 div; + u32 mfn, mfd = 1000000; + u32 max_mfd = 0x3FFFFFFF; + u64 temp64; + + if (rate > max_rate) + rate = max_rate; + else if (rate < min_rate) + rate = min_rate; + + if (parent_rate <= max_mfd) + mfd = parent_rate; + + div = rate / parent_rate; + temp64 = (u64) (rate - div * parent_rate); + temp64 *= mfd; + temp64 = div64_ul(temp64, parent_rate); + mfn = temp64; + + temp64 = (u64)parent_rate; + temp64 *= mfn; + do_div(temp64, mfd); + + return parent_rate * div + (unsigned long)temp64; +} + +static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + unsigned long min_rate = parent_rate * 27; + unsigned long max_rate = parent_rate * 54; + u32 val, div; + u32 mfn, mfd = 1000000; + u32 max_mfd = 0x3FFFFFFF; + u64 temp64; + + if (rate < min_rate || rate > max_rate) + return -EINVAL; + + if (parent_rate <= max_mfd) + mfd = parent_rate; + + div = rate / parent_rate; + temp64 = (u64) (rate - div * parent_rate); + temp64 *= mfd; + temp64 = div64_ul(temp64, parent_rate); + mfn = temp64; + + val = readl_relaxed(pll->base); + val &= ~pll->div_mask; + val |= div; + writel_relaxed(val, pll->base); + writel_relaxed(mfn, pll->base + pll->num_offset); + writel_relaxed(mfd, pll->base + pll->denom_offset); + + return clk_pllv3_wait_lock(pll); +} + +static const struct clk_ops clk_pllv3_av_ops = { + .prepare = clk_pllv3_prepare, + .unprepare = clk_pllv3_unprepare, + .is_prepared = clk_pllv3_is_prepared, + .recalc_rate = clk_pllv3_av_recalc_rate, + .round_rate = clk_pllv3_av_round_rate, + .set_rate = clk_pllv3_av_set_rate, +}; + +struct clk_pllv3_vf610_mf { + u32 mfi; /* integer part, can be 20 or 22 */ + u32 mfn; /* numerator, 30-bit value */ + u32 mfd; /* denominator, 30-bit value, must be less than mfn */ +}; + +static unsigned long clk_pllv3_vf610_mf_to_rate(unsigned long parent_rate, + struct clk_pllv3_vf610_mf mf) +{ + u64 temp64; + + temp64 = parent_rate; + temp64 *= mf.mfn; + do_div(temp64, mf.mfd); + + return (parent_rate * mf.mfi) + temp64; +} + +static struct clk_pllv3_vf610_mf clk_pllv3_vf610_rate_to_mf( + unsigned long parent_rate, unsigned long rate) +{ + struct clk_pllv3_vf610_mf mf; + u64 temp64; + + mf.mfi = (rate >= 22 * parent_rate) ? 22 : 20; + mf.mfd = 0x3fffffff; /* use max supported value for best accuracy */ + + if (rate <= parent_rate * mf.mfi) + mf.mfn = 0; + else if (rate >= parent_rate * (mf.mfi + 1)) + mf.mfn = mf.mfd - 1; + else { + /* rate = parent_rate * (mfi + mfn/mfd) */ + temp64 = rate - parent_rate * mf.mfi; + temp64 *= mf.mfd; + temp64 = div64_ul(temp64, parent_rate); + mf.mfn = temp64; + } + + return mf; +} + +static unsigned long clk_pllv3_vf610_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + struct clk_pllv3_vf610_mf mf; + + mf.mfn = readl_relaxed(pll->base + pll->num_offset); + mf.mfd = readl_relaxed(pll->base + pll->denom_offset); + mf.mfi = (readl_relaxed(pll->base) & pll->div_mask) ? 22 : 20; + + return clk_pllv3_vf610_mf_to_rate(parent_rate, mf); +} + +static long clk_pllv3_vf610_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_pllv3_vf610_mf mf = clk_pllv3_vf610_rate_to_mf(*prate, rate); + + return clk_pllv3_vf610_mf_to_rate(*prate, mf); +} + +static int clk_pllv3_vf610_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + struct clk_pllv3_vf610_mf mf = + clk_pllv3_vf610_rate_to_mf(parent_rate, rate); + u32 val; + + val = readl_relaxed(pll->base); + if (mf.mfi == 20) + val &= ~pll->div_mask; /* clear bit for mfi=20 */ + else + val |= pll->div_mask; /* set bit for mfi=22 */ + writel_relaxed(val, pll->base); + + writel_relaxed(mf.mfn, pll->base + pll->num_offset); + writel_relaxed(mf.mfd, pll->base + pll->denom_offset); + + return clk_pllv3_wait_lock(pll); +} + +static const struct clk_ops clk_pllv3_vf610_ops = { + .prepare = clk_pllv3_prepare, + .unprepare = clk_pllv3_unprepare, + .is_prepared = clk_pllv3_is_prepared, + .recalc_rate = clk_pllv3_vf610_recalc_rate, + .round_rate = clk_pllv3_vf610_round_rate, + .set_rate = clk_pllv3_vf610_set_rate, +}; + +static unsigned long clk_pllv3_enet_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv3 *pll = to_clk_pllv3(hw); + + return pll->ref_clock; +} + +static const struct clk_ops clk_pllv3_enet_ops = { + .prepare = clk_pllv3_prepare, + .unprepare = clk_pllv3_unprepare, + .is_prepared = clk_pllv3_is_prepared, + .recalc_rate = clk_pllv3_enet_recalc_rate, +}; + +struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, + u32 div_mask) +{ + struct clk_pllv3 *pll; + const struct clk_ops *ops; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->power_bit = BM_PLL_POWER; + pll->num_offset = PLL_NUM_OFFSET; + pll->denom_offset = PLL_DENOM_OFFSET; + + switch (type) { + case IMX_PLLV3_SYS: + ops = &clk_pllv3_sys_ops; + break; + case IMX_PLLV3_SYS_VF610: + ops = &clk_pllv3_vf610_ops; + pll->num_offset = PLL_VF610_NUM_OFFSET; + pll->denom_offset = PLL_VF610_DENOM_OFFSET; + break; + case IMX_PLLV3_USB_VF610: + pll->div_shift = 1; + fallthrough; + case IMX_PLLV3_USB: + ops = &clk_pllv3_ops; + pll->powerup_set = true; + break; + case IMX_PLLV3_AV_IMX7: + pll->num_offset = PLL_IMX7_NUM_OFFSET; + pll->denom_offset = PLL_IMX7_DENOM_OFFSET; + fallthrough; + case IMX_PLLV3_AV: + ops = &clk_pllv3_av_ops; + break; + case IMX_PLLV3_ENET_IMX7: + pll->power_bit = IMX7_ENET_PLL_POWER; + pll->ref_clock = 1000000000; + ops = &clk_pllv3_enet_ops; + break; + case IMX_PLLV3_ENET: + pll->ref_clock = 500000000; + ops = &clk_pllv3_enet_ops; + break; + case IMX_PLLV3_DDR_IMX7: + pll->power_bit = IMX7_DDR_PLL_POWER; + pll->num_offset = PLL_IMX7_NUM_OFFSET; + pll->denom_offset = PLL_IMX7_DENOM_OFFSET; + ops = &clk_pllv3_av_ops; + break; + default: + ops = &clk_pllv3_ops; + } + pll->base = base; + pll->div_mask = div_mask; + + init.name = name; + init.ops = ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + pll->hw.init = &init; + hw = &pll->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c new file mode 100644 index 000000000..9b136c951 --- /dev/null +++ b/drivers/clk/imx/clk-pllv4.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Author: Dong Aisheng <aisheng.dong@nxp.com> + * + */ + +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> + +#include "clk.h" + +/* PLL Control Status Register (xPLLCSR) */ +#define PLL_CSR_OFFSET 0x0 +#define PLL_VLD BIT(24) +#define PLL_EN BIT(0) + +/* PLL Configuration Register (xPLLCFG) */ +#define PLL_CFG_OFFSET 0x08 +#define IMX8ULP_PLL_CFG_OFFSET 0x10 +#define BP_PLL_MULT 16 +#define BM_PLL_MULT (0x7f << 16) + +/* PLL Numerator Register (xPLLNUM) */ +#define PLL_NUM_OFFSET 0x10 +#define IMX8ULP_PLL_NUM_OFFSET 0x1c + +/* PLL Denominator Register (xPLLDENOM) */ +#define PLL_DENOM_OFFSET 0x14 +#define IMX8ULP_PLL_DENOM_OFFSET 0x18 + +#define MAX_MFD 0x3fffffff +#define DEFAULT_MFD 1000000 + +struct clk_pllv4 { + struct clk_hw hw; + void __iomem *base; + u32 cfg_offset; + u32 num_offset; + u32 denom_offset; + bool use_mult_range; +}; + +/* Valid PLL MULT Table */ +static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16}; + +/* Valid PLL MULT range, (max, min) */ +static const int pllv4_mult_range[] = {54, 27}; + +#define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw) + +#define LOCK_TIMEOUT_US USEC_PER_MSEC + +static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll) +{ + u32 csr; + + return readl_poll_timeout(pll->base + PLL_CSR_OFFSET, + csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US); +} + +static int clk_pllv4_is_prepared(struct clk_hw *hw) +{ + struct clk_pllv4 *pll = to_clk_pllv4(hw); + + if (readl_relaxed(pll->base) & PLL_EN) + return 1; + + return 0; +} + +static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv4 *pll = to_clk_pllv4(hw); + u32 mult, mfn, mfd; + u64 temp64; + + mult = readl_relaxed(pll->base + pll->cfg_offset); + mult &= BM_PLL_MULT; + mult >>= BP_PLL_MULT; + + mfn = readl_relaxed(pll->base + pll->num_offset); + mfd = readl_relaxed(pll->base + pll->denom_offset); + temp64 = parent_rate; + temp64 *= mfn; + do_div(temp64, mfd); + + return (parent_rate * mult) + (u32)temp64; +} + +static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_pllv4 *pll = to_clk_pllv4(hw); + unsigned long parent_rate = *prate; + unsigned long round_rate, i; + u32 mfn, mfd = DEFAULT_MFD; + bool found = false; + u64 temp64; + u32 mult; + + if (pll->use_mult_range) { + temp64 = (u64)rate; + do_div(temp64, parent_rate); + mult = temp64; + if (mult >= pllv4_mult_range[1] && + mult <= pllv4_mult_range[0]) { + round_rate = parent_rate * mult; + found = true; + } + } else { + for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { + round_rate = parent_rate * pllv4_mult_table[i]; + if (rate >= round_rate) { + found = true; + break; + } + } + } + + if (!found) { + pr_warn("%s: unable to round rate %lu, parent rate %lu\n", + clk_hw_get_name(hw), rate, parent_rate); + return 0; + } + + if (parent_rate <= MAX_MFD) + mfd = parent_rate; + + temp64 = (u64)(rate - round_rate); + temp64 *= mfd; + do_div(temp64, parent_rate); + mfn = temp64; + + /* + * NOTE: The value of numerator must always be configured to be + * less than the value of the denominator. If we can't get a proper + * pair of mfn/mfd, we simply return the round_rate without using + * the frac part. + */ + if (mfn >= mfd) + return round_rate; + + temp64 = (u64)parent_rate; + temp64 *= mfn; + do_div(temp64, mfd); + + return round_rate + (u32)temp64; +} + +static bool clk_pllv4_is_valid_mult(struct clk_pllv4 *pll, unsigned int mult) +{ + int i; + + /* check if mult is in valid MULT table */ + if (pll->use_mult_range) { + if (mult >= pllv4_mult_range[1] && + mult <= pllv4_mult_range[0]) + return true; + } else { + for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { + if (pllv4_mult_table[i] == mult) + return true; + } + } + + return false; +} + +static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv4 *pll = to_clk_pllv4(hw); + u32 val, mult, mfn, mfd = DEFAULT_MFD; + u64 temp64; + + mult = rate / parent_rate; + + if (!clk_pllv4_is_valid_mult(pll, mult)) + return -EINVAL; + + if (parent_rate <= MAX_MFD) + mfd = parent_rate; + + temp64 = (u64)(rate - mult * parent_rate); + temp64 *= mfd; + do_div(temp64, parent_rate); + mfn = temp64; + + val = readl_relaxed(pll->base + pll->cfg_offset); + val &= ~BM_PLL_MULT; + val |= mult << BP_PLL_MULT; + writel_relaxed(val, pll->base + pll->cfg_offset); + + writel_relaxed(mfn, pll->base + pll->num_offset); + writel_relaxed(mfd, pll->base + pll->denom_offset); + + return 0; +} + +static int clk_pllv4_prepare(struct clk_hw *hw) +{ + u32 val; + struct clk_pllv4 *pll = to_clk_pllv4(hw); + + val = readl_relaxed(pll->base); + val |= PLL_EN; + writel_relaxed(val, pll->base); + + return clk_pllv4_wait_lock(pll); +} + +static void clk_pllv4_unprepare(struct clk_hw *hw) +{ + u32 val; + struct clk_pllv4 *pll = to_clk_pllv4(hw); + + val = readl_relaxed(pll->base); + val &= ~PLL_EN; + writel_relaxed(val, pll->base); +} + +static const struct clk_ops clk_pllv4_ops = { + .recalc_rate = clk_pllv4_recalc_rate, + .round_rate = clk_pllv4_round_rate, + .set_rate = clk_pllv4_set_rate, + .prepare = clk_pllv4_prepare, + .unprepare = clk_pllv4_unprepare, + .is_prepared = clk_pllv4_is_prepared, +}; + +struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name, + const char *parent_name, void __iomem *base) +{ + struct clk_pllv4 *pll; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + + if (type == IMX_PLLV4_IMX8ULP || + type == IMX_PLLV4_IMX8ULP_1GHZ) { + pll->cfg_offset = IMX8ULP_PLL_CFG_OFFSET; + pll->num_offset = IMX8ULP_PLL_NUM_OFFSET; + pll->denom_offset = IMX8ULP_PLL_DENOM_OFFSET; + if (type == IMX_PLLV4_IMX8ULP_1GHZ) + pll->use_mult_range = true; + } else { + pll->cfg_offset = PLL_CFG_OFFSET; + pll->num_offset = PLL_NUM_OFFSET; + pll->denom_offset = PLL_DENOM_OFFSET; + } + + init.name = name; + init.ops = &clk_pllv4_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; + + pll->hw.init = &init; + + hw = &pll->hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + hw = ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx_clk_hw_pllv4); diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c new file mode 100644 index 000000000..db307890e --- /dev/null +++ b/drivers/clk/imx/clk-scu.c @@ -0,0 +1,894 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018-2021 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/arm-smccc.h> +#include <linux/bsearch.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#include "clk-scu.h" + +#define IMX_SIP_CPUFREQ 0xC2000001 +#define IMX_SIP_SET_CPUFREQ 0x00 + +static struct imx_sc_ipc *ccm_ipc_handle; +static struct device_node *pd_np; +static struct platform_driver imx_clk_scu_driver; +static const struct imx_clk_scu_rsrc_table *rsrc_table; + +struct imx_scu_clk_node { + const char *name; + u32 rsrc; + u8 clk_type; + const char * const *parents; + int num_parents; + + struct clk_hw *hw; + struct list_head node; +}; + +struct list_head imx_scu_clks[IMX_SC_R_LAST]; + +/* + * struct clk_scu - Description of one SCU clock + * @hw: the common clk_hw + * @rsrc_id: resource ID of this SCU clock + * @clk_type: type of this clock resource + */ +struct clk_scu { + struct clk_hw hw; + u16 rsrc_id; + u8 clk_type; + + /* for state save&restore */ + struct clk_hw *parent; + u8 parent_index; + bool is_enabled; + u32 rate; +}; + +/* + * struct clk_gpr_scu - Description of one SCU GPR clock + * @hw: the common clk_hw + * @rsrc_id: resource ID of this SCU clock + * @gpr_id: GPR ID index to control the divider + */ +struct clk_gpr_scu { + struct clk_hw hw; + u16 rsrc_id; + u8 gpr_id; + u8 flags; + bool gate_invert; +}; + +#define to_clk_gpr_scu(_hw) container_of(_hw, struct clk_gpr_scu, hw) + +/* + * struct imx_sc_msg_req_set_clock_rate - clock set rate protocol + * @hdr: SCU protocol header + * @rate: rate to set + * @resource: clock resource to set rate + * @clk: clk type of this resource + * + * This structure describes the SCU protocol of clock rate set + */ +struct imx_sc_msg_req_set_clock_rate { + struct imx_sc_rpc_msg hdr; + __le32 rate; + __le16 resource; + u8 clk; +} __packed __aligned(4); + +struct req_get_clock_rate { + __le16 resource; + u8 clk; +} __packed __aligned(4); + +struct resp_get_clock_rate { + __le32 rate; +}; + +/* + * struct imx_sc_msg_get_clock_rate - clock get rate protocol + * @hdr: SCU protocol header + * @req: get rate request protocol + * @resp: get rate response protocol + * + * This structure describes the SCU protocol of clock rate get + */ +struct imx_sc_msg_get_clock_rate { + struct imx_sc_rpc_msg hdr; + union { + struct req_get_clock_rate req; + struct resp_get_clock_rate resp; + } data; +}; + +/* + * struct imx_sc_msg_get_clock_parent - clock get parent protocol + * @hdr: SCU protocol header + * @req: get parent request protocol + * @resp: get parent response protocol + * + * This structure describes the SCU protocol of clock get parent + */ +struct imx_sc_msg_get_clock_parent { + struct imx_sc_rpc_msg hdr; + union { + struct req_get_clock_parent { + __le16 resource; + u8 clk; + } __packed __aligned(4) req; + struct resp_get_clock_parent { + u8 parent; + } resp; + } data; +}; + +/* + * struct imx_sc_msg_set_clock_parent - clock set parent protocol + * @hdr: SCU protocol header + * @req: set parent request protocol + * + * This structure describes the SCU protocol of clock set parent + */ +struct imx_sc_msg_set_clock_parent { + struct imx_sc_rpc_msg hdr; + __le16 resource; + u8 clk; + u8 parent; +} __packed; + +/* + * struct imx_sc_msg_req_clock_enable - clock gate protocol + * @hdr: SCU protocol header + * @resource: clock resource to gate + * @clk: clk type of this resource + * @enable: whether gate off the clock + * @autog: HW auto gate enable + * + * This structure describes the SCU protocol of clock gate + */ +struct imx_sc_msg_req_clock_enable { + struct imx_sc_rpc_msg hdr; + __le16 resource; + u8 clk; + u8 enable; + u8 autog; +} __packed __aligned(4); + +static inline struct clk_scu *to_clk_scu(struct clk_hw *hw) +{ + return container_of(hw, struct clk_scu, hw); +} + +static inline int imx_scu_clk_search_cmp(const void *rsrc, const void *rsrc_p) +{ + return *(u32 *)rsrc - *(u32 *)rsrc_p; +} + +static bool imx_scu_clk_is_valid(u32 rsrc_id) +{ + void *p; + + if (!rsrc_table) + return true; + + p = bsearch(&rsrc_id, rsrc_table->rsrc, rsrc_table->num, + sizeof(rsrc_table->rsrc[0]), imx_scu_clk_search_cmp); + + return p != NULL; +} + +int imx_clk_scu_init(struct device_node *np, + const struct imx_clk_scu_rsrc_table *data) +{ + u32 clk_cells; + int ret, i; + + ret = imx_scu_get_handle(&ccm_ipc_handle); + if (ret) + return ret; + + of_property_read_u32(np, "#clock-cells", &clk_cells); + + if (clk_cells == 2) { + for (i = 0; i < IMX_SC_R_LAST; i++) + INIT_LIST_HEAD(&imx_scu_clks[i]); + + /* pd_np will be used to attach power domains later */ + pd_np = of_find_compatible_node(NULL, NULL, "fsl,scu-pd"); + if (!pd_np) + return -EINVAL; + + rsrc_table = data; + } + + return platform_driver_register(&imx_clk_scu_driver); +} + +/* + * clk_scu_recalc_rate - Get clock rate for a SCU clock + * @hw: clock to get rate for + * @parent_rate: parent rate provided by common clock framework, not used + * + * Gets the current clock rate of a SCU clock. Returns the current + * clock rate, or zero in failure. + */ +static unsigned long clk_scu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_get_clock_rate msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_RATE; + hdr->size = 2; + + msg.data.req.resource = cpu_to_le16(clk->rsrc_id); + msg.data.req.clk = clk->clk_type; + + ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); + if (ret) { + pr_err("%s: failed to get clock rate %d\n", + clk_hw_get_name(hw), ret); + return 0; + } + + return le32_to_cpu(msg.data.resp.rate); +} + +/* + * clk_scu_round_rate - Round clock rate for a SCU clock + * @hw: clock to round rate for + * @rate: rate to round + * @parent_rate: parent rate provided by common clock framework, not used + * + * Returns the current clock rate, or zero in failure. + */ +static long clk_scu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + /* + * Assume we support all the requested rate and let the SCU firmware + * to handle the left work + */ + return rate; +} + +static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct arm_smccc_res res; + unsigned long cluster_id; + + if (clk->rsrc_id == IMX_SC_R_A35 || clk->rsrc_id == IMX_SC_R_A53) + cluster_id = 0; + else if (clk->rsrc_id == IMX_SC_R_A72) + cluster_id = 1; + else + return -EINVAL; + + /* CPU frequency scaling can ONLY be done by ARM-Trusted-Firmware */ + arm_smccc_smc(IMX_SIP_CPUFREQ, IMX_SIP_SET_CPUFREQ, + cluster_id, rate, 0, 0, 0, 0, &res); + + return 0; +} + +/* + * clk_scu_set_rate - Set rate for a SCU clock + * @hw: clock to change rate for + * @rate: target rate for the clock + * @parent_rate: rate of the clock parent, not used for SCU clocks + * + * Sets a clock frequency for a SCU clock. Returns the SCU + * protocol status. + */ +static int clk_scu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_req_set_clock_rate msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_RATE; + hdr->size = 3; + + msg.rate = cpu_to_le32(rate); + msg.resource = cpu_to_le16(clk->rsrc_id); + msg.clk = clk->clk_type; + + return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); +} + +static u8 clk_scu_get_parent(struct clk_hw *hw) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_get_clock_parent msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_GET_CLOCK_PARENT; + hdr->size = 2; + + msg.data.req.resource = cpu_to_le16(clk->rsrc_id); + msg.data.req.clk = clk->clk_type; + + ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); + if (ret) { + pr_err("%s: failed to get clock parent %d\n", + clk_hw_get_name(hw), ret); + return 0; + } + + clk->parent_index = msg.data.resp.parent; + + return msg.data.resp.parent; +} + +static int clk_scu_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_scu *clk = to_clk_scu(hw); + struct imx_sc_msg_set_clock_parent msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_SET_CLOCK_PARENT; + hdr->size = 2; + + msg.resource = cpu_to_le16(clk->rsrc_id); + msg.clk = clk->clk_type; + msg.parent = index; + + ret = imx_scu_call_rpc(ccm_ipc_handle, &msg, true); + if (ret) { + pr_err("%s: failed to set clock parent %d\n", + clk_hw_get_name(hw), ret); + return ret; + } + + clk->parent_index = index; + + return 0; +} + +static int sc_pm_clock_enable(struct imx_sc_ipc *ipc, u16 resource, + u8 clk, bool enable, bool autog) +{ + struct imx_sc_msg_req_clock_enable msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_CLOCK_ENABLE; + hdr->size = 3; + + msg.resource = cpu_to_le16(resource); + msg.clk = clk; + msg.enable = enable; + msg.autog = autog; + + return imx_scu_call_rpc(ccm_ipc_handle, &msg, true); +} + +/* + * clk_scu_prepare - Enable a SCU clock + * @hw: clock to enable + * + * Enable the clock at the DSC slice level + */ +static int clk_scu_prepare(struct clk_hw *hw) +{ + struct clk_scu *clk = to_clk_scu(hw); + + return sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, + clk->clk_type, true, false); +} + +/* + * clk_scu_unprepare - Disable a SCU clock + * @hw: clock to enable + * + * Disable the clock at the DSC slice level + */ +static void clk_scu_unprepare(struct clk_hw *hw) +{ + struct clk_scu *clk = to_clk_scu(hw); + int ret; + + ret = sc_pm_clock_enable(ccm_ipc_handle, clk->rsrc_id, + clk->clk_type, false, false); + if (ret) + pr_warn("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), + ret); +} + +static const struct clk_ops clk_scu_ops = { + .recalc_rate = clk_scu_recalc_rate, + .round_rate = clk_scu_round_rate, + .set_rate = clk_scu_set_rate, + .get_parent = clk_scu_get_parent, + .set_parent = clk_scu_set_parent, + .prepare = clk_scu_prepare, + .unprepare = clk_scu_unprepare, +}; + +static const struct clk_ops clk_scu_cpu_ops = { + .recalc_rate = clk_scu_recalc_rate, + .round_rate = clk_scu_round_rate, + .set_rate = clk_scu_atf_set_cpu_rate, + .prepare = clk_scu_prepare, + .unprepare = clk_scu_unprepare, +}; + +static const struct clk_ops clk_scu_pi_ops = { + .recalc_rate = clk_scu_recalc_rate, + .round_rate = clk_scu_round_rate, + .set_rate = clk_scu_set_rate, +}; + +struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, + const char * const *parents, int num_parents, + u32 rsrc_id, u8 clk_type) +{ + struct clk_init_data init; + struct clk_scu *clk; + struct clk_hw *hw; + int ret; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) + return ERR_PTR(-ENOMEM); + + clk->rsrc_id = rsrc_id; + clk->clk_type = clk_type; + + init.name = name; + init.ops = &clk_scu_ops; + if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72) + init.ops = &clk_scu_cpu_ops; + else if (rsrc_id == IMX_SC_R_PI_0_PLL) + init.ops = &clk_scu_pi_ops; + else + init.ops = &clk_scu_ops; + init.parent_names = parents; + init.num_parents = num_parents; + + /* + * Note on MX8, the clocks are tightly coupled with power domain + * that once the power domain is off, the clock status may be + * lost. So we make it NOCACHE to let user to retrieve the real + * clock status from HW instead of using the possible invalid + * cached rate. + */ + init.flags = CLK_GET_RATE_NOCACHE; + clk->hw.init = &init; + + hw = &clk->hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(clk); + hw = ERR_PTR(ret); + return hw; + } + + if (dev) + dev_set_drvdata(dev, clk); + + return hw; +} + +struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec, + void *data) +{ + unsigned int rsrc = clkspec->args[0]; + unsigned int idx = clkspec->args[1]; + struct list_head *scu_clks = data; + struct imx_scu_clk_node *clk; + + list_for_each_entry(clk, &scu_clks[rsrc], node) { + if (clk->clk_type == idx) + return clk->hw; + } + + return ERR_PTR(-ENODEV); +} + +static int imx_clk_scu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imx_scu_clk_node *clk = dev_get_platdata(dev); + struct clk_hw *hw; + int ret; + + if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) || + (clk->rsrc == IMX_SC_R_A72))) { + pm_runtime_set_suspended(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(dev); + + ret = pm_runtime_resume_and_get(dev); + if (ret) { + pm_genpd_remove_device(dev); + pm_runtime_disable(dev); + return ret; + } + } + + hw = __imx_clk_scu(dev, clk->name, clk->parents, clk->num_parents, + clk->rsrc, clk->clk_type); + if (IS_ERR(hw)) { + pm_runtime_disable(dev); + return PTR_ERR(hw); + } + + clk->hw = hw; + list_add_tail(&clk->node, &imx_scu_clks[clk->rsrc]); + + if (!((clk->rsrc == IMX_SC_R_A35) || (clk->rsrc == IMX_SC_R_A53) || + (clk->rsrc == IMX_SC_R_A72))) { + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + } + + dev_dbg(dev, "register SCU clock rsrc:%d type:%d\n", clk->rsrc, + clk->clk_type); + + return 0; +} + +static int __maybe_unused imx_clk_scu_suspend(struct device *dev) +{ + struct clk_scu *clk = dev_get_drvdata(dev); + u32 rsrc_id = clk->rsrc_id; + + if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) || + (rsrc_id == IMX_SC_R_A72)) + return 0; + + clk->parent = clk_hw_get_parent(&clk->hw); + + /* DC SS needs to handle bypass clock using non-cached clock rate */ + if (clk->rsrc_id == IMX_SC_R_DC_0_VIDEO0 || + clk->rsrc_id == IMX_SC_R_DC_0_VIDEO1 || + clk->rsrc_id == IMX_SC_R_DC_1_VIDEO0 || + clk->rsrc_id == IMX_SC_R_DC_1_VIDEO1) + clk->rate = clk_scu_recalc_rate(&clk->hw, 0); + else + clk->rate = clk_hw_get_rate(&clk->hw); + clk->is_enabled = clk_hw_is_enabled(&clk->hw); + + if (clk->parent) + dev_dbg(dev, "save parent %s idx %u\n", clk_hw_get_name(clk->parent), + clk->parent_index); + + if (clk->rate) + dev_dbg(dev, "save rate %d\n", clk->rate); + + if (clk->is_enabled) + dev_dbg(dev, "save enabled state\n"); + + return 0; +} + +static int __maybe_unused imx_clk_scu_resume(struct device *dev) +{ + struct clk_scu *clk = dev_get_drvdata(dev); + u32 rsrc_id = clk->rsrc_id; + int ret = 0; + + if ((rsrc_id == IMX_SC_R_A35) || (rsrc_id == IMX_SC_R_A53) || + (rsrc_id == IMX_SC_R_A72)) + return 0; + + if (clk->parent) { + ret = clk_scu_set_parent(&clk->hw, clk->parent_index); + dev_dbg(dev, "restore parent %s idx %u %s\n", + clk_hw_get_name(clk->parent), + clk->parent_index, !ret ? "success" : "failed"); + } + + if (clk->rate) { + ret = clk_scu_set_rate(&clk->hw, clk->rate, 0); + dev_dbg(dev, "restore rate %d %s\n", clk->rate, + !ret ? "success" : "failed"); + } + + if (clk->is_enabled && rsrc_id != IMX_SC_R_PI_0_PLL) { + ret = clk_scu_prepare(&clk->hw); + dev_dbg(dev, "restore enabled state %s\n", + !ret ? "success" : "failed"); + } + + return ret; +} + +static const struct dev_pm_ops imx_clk_scu_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_scu_suspend, + imx_clk_scu_resume) +}; + +static struct platform_driver imx_clk_scu_driver = { + .driver = { + .name = "imx-scu-clk", + .suppress_bind_attrs = true, + .pm = &imx_clk_scu_pm_ops, + }, + .probe = imx_clk_scu_probe, +}; + +static int imx_clk_scu_attach_pd(struct device *dev, u32 rsrc_id) +{ + struct of_phandle_args genpdspec = { + .np = pd_np, + .args_count = 1, + .args[0] = rsrc_id, + }; + + if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || + rsrc_id == IMX_SC_R_A72) + return 0; + + return of_genpd_add_device(&genpdspec, dev); +} + +struct clk_hw *imx_clk_scu_alloc_dev(const char *name, + const char * const *parents, + int num_parents, u32 rsrc_id, u8 clk_type) +{ + struct imx_scu_clk_node clk = { + .name = name, + .rsrc = rsrc_id, + .clk_type = clk_type, + .parents = parents, + .num_parents = num_parents, + }; + struct platform_device *pdev; + int ret; + + if (!imx_scu_clk_is_valid(rsrc_id)) + return ERR_PTR(-EINVAL); + + pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); + if (!pdev) { + pr_err("%s: failed to allocate scu clk dev rsrc %d type %d\n", + name, rsrc_id, clk_type); + return ERR_PTR(-ENOMEM); + } + + ret = platform_device_add_data(pdev, &clk, sizeof(clk)); + if (ret) { + platform_device_put(pdev); + return ERR_PTR(ret); + } + + ret = driver_set_override(&pdev->dev, &pdev->driver_override, + "imx-scu-clk", strlen("imx-scu-clk")); + if (ret) { + platform_device_put(pdev); + return ERR_PTR(ret); + } + + ret = imx_clk_scu_attach_pd(&pdev->dev, rsrc_id); + if (ret) + pr_warn("%s: failed to attached the power domain %d\n", + name, ret); + + ret = platform_device_add(pdev); + if (ret) { + platform_device_put(pdev); + return ERR_PTR(ret); + } + + /* For API backwards compatiblilty, simply return NULL for success */ + return NULL; +} + +void imx_clk_scu_unregister(void) +{ + struct imx_scu_clk_node *clk, *n; + int i; + + for (i = 0; i < IMX_SC_R_LAST; i++) { + list_for_each_entry_safe(clk, n, &imx_scu_clks[i], node) { + clk_hw_unregister(clk->hw); + kfree(clk); + } + } +} + +static unsigned long clk_gpr_div_scu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); + unsigned long rate = 0; + u32 val; + int err; + + err = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, + clk->gpr_id, &val); + + rate = val ? parent_rate / 2 : parent_rate; + + return err ? 0 : rate; +} + +static long clk_gpr_div_scu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + if (rate < *prate) + rate = *prate / 2; + else + rate = *prate; + + return rate; +} + +static int clk_gpr_div_scu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); + uint32_t val; + int err; + + val = (rate < parent_rate) ? 1 : 0; + err = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, + clk->gpr_id, val); + + return err ? -EINVAL : 0; +} + +static const struct clk_ops clk_gpr_div_scu_ops = { + .recalc_rate = clk_gpr_div_scu_recalc_rate, + .round_rate = clk_gpr_div_scu_round_rate, + .set_rate = clk_gpr_div_scu_set_rate, +}; + +static u8 clk_gpr_mux_scu_get_parent(struct clk_hw *hw) +{ + struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); + u32 val = 0; + + imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, + clk->gpr_id, &val); + + return (u8)val; +} + +static int clk_gpr_mux_scu_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); + + return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, + clk->gpr_id, index); +} + +static const struct clk_ops clk_gpr_mux_scu_ops = { + .get_parent = clk_gpr_mux_scu_get_parent, + .set_parent = clk_gpr_mux_scu_set_parent, +}; + +static int clk_gpr_gate_scu_prepare(struct clk_hw *hw) +{ + struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); + + return imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, + clk->gpr_id, !clk->gate_invert); +} + +static void clk_gpr_gate_scu_unprepare(struct clk_hw *hw) +{ + struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); + int ret; + + ret = imx_sc_misc_set_control(ccm_ipc_handle, clk->rsrc_id, + clk->gpr_id, clk->gate_invert); + if (ret) + pr_err("%s: clk unprepare failed %d\n", clk_hw_get_name(hw), + ret); +} + +static int clk_gpr_gate_scu_is_prepared(struct clk_hw *hw) +{ + struct clk_gpr_scu *clk = to_clk_gpr_scu(hw); + int ret; + u32 val; + + ret = imx_sc_misc_get_control(ccm_ipc_handle, clk->rsrc_id, + clk->gpr_id, &val); + if (ret) + return ret; + + return clk->gate_invert ? !val : val; +} + +static const struct clk_ops clk_gpr_gate_scu_ops = { + .prepare = clk_gpr_gate_scu_prepare, + .unprepare = clk_gpr_gate_scu_unprepare, + .is_prepared = clk_gpr_gate_scu_is_prepared, +}; + +struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name, + int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags, + bool invert) +{ + struct imx_scu_clk_node *clk_node; + struct clk_gpr_scu *clk; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + if (rsrc_id >= IMX_SC_R_LAST || gpr_id >= IMX_SC_C_LAST) + return ERR_PTR(-EINVAL); + + clk_node = kzalloc(sizeof(*clk_node), GFP_KERNEL); + if (!clk_node) + return ERR_PTR(-ENOMEM); + + if (!imx_scu_clk_is_valid(rsrc_id)) { + kfree(clk_node); + return ERR_PTR(-EINVAL); + } + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) { + kfree(clk_node); + return ERR_PTR(-ENOMEM); + } + + clk->rsrc_id = rsrc_id; + clk->gpr_id = gpr_id; + clk->flags = flags; + clk->gate_invert = invert; + + if (flags & IMX_SCU_GPR_CLK_GATE) + init.ops = &clk_gpr_gate_scu_ops; + + if (flags & IMX_SCU_GPR_CLK_DIV) + init.ops = &clk_gpr_div_scu_ops; + + if (flags & IMX_SCU_GPR_CLK_MUX) + init.ops = &clk_gpr_mux_scu_ops; + + init.flags = 0; + init.name = name; + init.parent_names = parent_name; + init.num_parents = num_parents; + + clk->hw.init = &init; + + hw = &clk->hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(clk); + kfree(clk_node); + hw = ERR_PTR(ret); + } else { + clk_node->hw = hw; + clk_node->clk_type = gpr_id; + list_add_tail(&clk_node->node, &imx_scu_clks[rsrc_id]); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-scu.h b/drivers/clk/imx/clk-scu.h new file mode 100644 index 000000000..af7b697f5 --- /dev/null +++ b/drivers/clk/imx/clk-scu.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018-2021 NXP + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#ifndef __IMX_CLK_SCU_H +#define __IMX_CLK_SCU_H + +#include <linux/firmware/imx/sci.h> +#include <linux/of.h> + +#define IMX_SCU_GPR_CLK_GATE BIT(0) +#define IMX_SCU_GPR_CLK_DIV BIT(1) +#define IMX_SCU_GPR_CLK_MUX BIT(2) + +struct imx_clk_scu_rsrc_table { + const u32 *rsrc; + u8 num; +}; + +extern struct list_head imx_scu_clks[]; +extern const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops; +extern const struct imx_clk_scu_rsrc_table imx_clk_scu_rsrc_imx8dxl; +extern const struct imx_clk_scu_rsrc_table imx_clk_scu_rsrc_imx8qxp; +extern const struct imx_clk_scu_rsrc_table imx_clk_scu_rsrc_imx8qm; + +int imx_clk_scu_init(struct device_node *np, + const struct imx_clk_scu_rsrc_table *data); +struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec, + void *data); +struct clk_hw *imx_clk_scu_alloc_dev(const char *name, + const char * const *parents, + int num_parents, u32 rsrc_id, u8 clk_type); + +struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, + const char * const *parents, int num_parents, + u32 rsrc_id, u8 clk_type); + +void imx_clk_scu_unregister(void); + +struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, bool hw_gate); +void imx_clk_lpcg_scu_unregister(struct clk_hw *hw); + +struct clk_hw *__imx_clk_gpr_scu(const char *name, const char * const *parent_name, + int num_parents, u32 rsrc_id, u8 gpr_id, u8 flags, + bool invert); + +static inline struct clk_hw *imx_clk_scu(const char *name, u32 rsrc_id, + u8 clk_type) +{ + return imx_clk_scu_alloc_dev(name, NULL, 0, rsrc_id, clk_type); +} + +static inline struct clk_hw *imx_clk_scu2(const char *name, const char * const *parents, + int num_parents, u32 rsrc_id, u8 clk_type) +{ + return imx_clk_scu_alloc_dev(name, parents, num_parents, rsrc_id, clk_type); +} + +static inline struct clk_hw *imx_clk_lpcg_scu_dev(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, bool hw_gate) +{ + return __imx_clk_lpcg_scu(dev, name, parent_name, flags, reg, + bit_idx, hw_gate); +} + +static inline struct clk_hw *imx_clk_lpcg_scu(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + u8 bit_idx, bool hw_gate) +{ + return __imx_clk_lpcg_scu(NULL, name, parent_name, flags, reg, + bit_idx, hw_gate); +} + +static inline struct clk_hw *imx_clk_gate_gpr_scu(const char *name, const char *parent_name, + u32 rsrc_id, u8 gpr_id, bool invert) +{ + return __imx_clk_gpr_scu(name, &parent_name, 1, rsrc_id, gpr_id, + IMX_SCU_GPR_CLK_GATE, invert); +} + +static inline struct clk_hw *imx_clk_divider_gpr_scu(const char *name, const char *parent_name, + u32 rsrc_id, u8 gpr_id) +{ + return __imx_clk_gpr_scu(name, &parent_name, 1, rsrc_id, gpr_id, + IMX_SCU_GPR_CLK_DIV, 0); +} + +static inline struct clk_hw *imx_clk_mux_gpr_scu(const char *name, const char * const *parent_names, + int num_parents, u32 rsrc_id, u8 gpr_id) +{ + return __imx_clk_gpr_scu(name, parent_names, num_parents, rsrc_id, + gpr_id, IMX_SCU_GPR_CLK_MUX, 0); +} +#endif diff --git a/drivers/clk/imx/clk-sscg-pll.c b/drivers/clk/imx/clk-sscg-pll.c new file mode 100644 index 000000000..81f304fae --- /dev/null +++ b/drivers/clk/imx/clk-sscg-pll.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright 2018 NXP. + * + * This driver supports the SCCG plls found in the imx8m SOCs + * + * Documentation for this SCCG pll can be found at: + * https://www.nxp.com/docs/en/reference-manual/IMX8MDQLQRM.pdf#page=834 + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/slab.h> +#include <linux/bitfield.h> + +#include "clk.h" + +/* PLL CFGs */ +#define PLL_CFG0 0x0 +#define PLL_CFG1 0x4 +#define PLL_CFG2 0x8 + +#define PLL_DIVF1_MASK GENMASK(18, 13) +#define PLL_DIVF2_MASK GENMASK(12, 7) +#define PLL_DIVR1_MASK GENMASK(27, 25) +#define PLL_DIVR2_MASK GENMASK(24, 19) +#define PLL_DIVQ_MASK GENMASK(6, 1) +#define PLL_REF_MASK GENMASK(2, 0) + +#define PLL_LOCK_MASK BIT(31) +#define PLL_PD_MASK BIT(7) + +/* These are the specification limits for the SSCG PLL */ +#define PLL_REF_MIN_FREQ 25000000UL +#define PLL_REF_MAX_FREQ 235000000UL + +#define PLL_STAGE1_MIN_FREQ 1600000000UL +#define PLL_STAGE1_MAX_FREQ 2400000000UL + +#define PLL_STAGE1_REF_MIN_FREQ 25000000UL +#define PLL_STAGE1_REF_MAX_FREQ 54000000UL + +#define PLL_STAGE2_MIN_FREQ 1200000000UL +#define PLL_STAGE2_MAX_FREQ 2400000000UL + +#define PLL_STAGE2_REF_MIN_FREQ 54000000UL +#define PLL_STAGE2_REF_MAX_FREQ 75000000UL + +#define PLL_OUT_MIN_FREQ 20000000UL +#define PLL_OUT_MAX_FREQ 1200000000UL + +#define PLL_DIVR1_MAX 7 +#define PLL_DIVR2_MAX 63 +#define PLL_DIVF1_MAX 63 +#define PLL_DIVF2_MAX 63 +#define PLL_DIVQ_MAX 63 + +#define PLL_BYPASS_NONE 0x0 +#define PLL_BYPASS1 0x2 +#define PLL_BYPASS2 0x1 + +#define SSCG_PLL_BYPASS1_MASK BIT(5) +#define SSCG_PLL_BYPASS2_MASK BIT(4) +#define SSCG_PLL_BYPASS_MASK GENMASK(5, 4) + +#define PLL_SCCG_LOCK_TIMEOUT 70 + +struct clk_sscg_pll_setup { + int divr1, divf1; + int divr2, divf2; + int divq; + int bypass; + uint64_t vco1; + uint64_t vco2; + uint64_t fout; + uint64_t ref; + uint64_t ref_div1; + uint64_t ref_div2; + uint64_t fout_request; + int fout_error; +}; + +struct clk_sscg_pll { + struct clk_hw hw; + const struct clk_ops ops; + void __iomem *base; + struct clk_sscg_pll_setup setup; + u8 parent; + u8 bypass1; + u8 bypass2; +}; + +#define to_clk_sscg_pll(_hw) container_of(_hw, struct clk_sscg_pll, hw) + +static int clk_sscg_pll_wait_lock(struct clk_sscg_pll *pll) +{ + u32 val; + + val = readl_relaxed(pll->base + PLL_CFG0); + + /* don't wait for lock if all plls are bypassed */ + if (!(val & SSCG_PLL_BYPASS2_MASK)) + return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, + 0, PLL_SCCG_LOCK_TIMEOUT); + + return 0; +} + +static int clk_sscg_pll2_check_match(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup) +{ + int new_diff = temp_setup->fout - temp_setup->fout_request; + int diff = temp_setup->fout_error; + + if (abs(diff) > abs(new_diff)) { + temp_setup->fout_error = new_diff; + memcpy(setup, temp_setup, sizeof(struct clk_sscg_pll_setup)); + + if (temp_setup->fout_request == temp_setup->fout) + return 0; + } + return -1; +} + +static int clk_sscg_divq_lookup(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup) +{ + int ret = -EINVAL; + + for (temp_setup->divq = 0; temp_setup->divq <= PLL_DIVQ_MAX; + temp_setup->divq++) { + temp_setup->vco2 = temp_setup->vco1; + do_div(temp_setup->vco2, temp_setup->divr2 + 1); + temp_setup->vco2 *= 2; + temp_setup->vco2 *= temp_setup->divf2 + 1; + if (temp_setup->vco2 >= PLL_STAGE2_MIN_FREQ && + temp_setup->vco2 <= PLL_STAGE2_MAX_FREQ) { + temp_setup->fout = temp_setup->vco2; + do_div(temp_setup->fout, 2 * (temp_setup->divq + 1)); + + ret = clk_sscg_pll2_check_match(setup, temp_setup); + if (!ret) { + temp_setup->bypass = PLL_BYPASS1; + return ret; + } + } + } + + return ret; +} + +static int clk_sscg_divf2_lookup(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup) +{ + int ret = -EINVAL; + + for (temp_setup->divf2 = 0; temp_setup->divf2 <= PLL_DIVF2_MAX; + temp_setup->divf2++) { + ret = clk_sscg_divq_lookup(setup, temp_setup); + if (!ret) + return ret; + } + + return ret; +} + +static int clk_sscg_divr2_lookup(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup) +{ + int ret = -EINVAL; + + for (temp_setup->divr2 = 0; temp_setup->divr2 <= PLL_DIVR2_MAX; + temp_setup->divr2++) { + temp_setup->ref_div2 = temp_setup->vco1; + do_div(temp_setup->ref_div2, temp_setup->divr2 + 1); + if (temp_setup->ref_div2 >= PLL_STAGE2_REF_MIN_FREQ && + temp_setup->ref_div2 <= PLL_STAGE2_REF_MAX_FREQ) { + ret = clk_sscg_divf2_lookup(setup, temp_setup); + if (!ret) + return ret; + } + } + + return ret; +} + +static int clk_sscg_pll2_find_setup(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup, + uint64_t ref) +{ + int ret; + + if (ref < PLL_STAGE1_MIN_FREQ || ref > PLL_STAGE1_MAX_FREQ) + return -EINVAL; + + temp_setup->vco1 = ref; + + ret = clk_sscg_divr2_lookup(setup, temp_setup); + return ret; +} + +static int clk_sscg_divf1_lookup(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup) +{ + int ret = -EINVAL; + + for (temp_setup->divf1 = 0; temp_setup->divf1 <= PLL_DIVF1_MAX; + temp_setup->divf1++) { + uint64_t vco1 = temp_setup->ref; + + do_div(vco1, temp_setup->divr1 + 1); + vco1 *= 2; + vco1 *= temp_setup->divf1 + 1; + + ret = clk_sscg_pll2_find_setup(setup, temp_setup, vco1); + if (!ret) { + temp_setup->bypass = PLL_BYPASS_NONE; + return ret; + } + } + + return ret; +} + +static int clk_sscg_divr1_lookup(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup) +{ + int ret = -EINVAL; + + for (temp_setup->divr1 = 0; temp_setup->divr1 <= PLL_DIVR1_MAX; + temp_setup->divr1++) { + temp_setup->ref_div1 = temp_setup->ref; + do_div(temp_setup->ref_div1, temp_setup->divr1 + 1); + if (temp_setup->ref_div1 >= PLL_STAGE1_REF_MIN_FREQ && + temp_setup->ref_div1 <= PLL_STAGE1_REF_MAX_FREQ) { + ret = clk_sscg_divf1_lookup(setup, temp_setup); + if (!ret) + return ret; + } + } + + return ret; +} + +static int clk_sscg_pll1_find_setup(struct clk_sscg_pll_setup *setup, + struct clk_sscg_pll_setup *temp_setup, + uint64_t ref) +{ + int ret; + + if (ref < PLL_REF_MIN_FREQ || ref > PLL_REF_MAX_FREQ) + return -EINVAL; + + temp_setup->ref = ref; + + ret = clk_sscg_divr1_lookup(setup, temp_setup); + + return ret; +} + +static int clk_sscg_pll_find_setup(struct clk_sscg_pll_setup *setup, + uint64_t prate, + uint64_t rate, int try_bypass) +{ + struct clk_sscg_pll_setup temp_setup; + int ret = -EINVAL; + + memset(&temp_setup, 0, sizeof(struct clk_sscg_pll_setup)); + memset(setup, 0, sizeof(struct clk_sscg_pll_setup)); + + temp_setup.fout_error = PLL_OUT_MAX_FREQ; + temp_setup.fout_request = rate; + + switch (try_bypass) { + case PLL_BYPASS2: + if (prate == rate) { + setup->bypass = PLL_BYPASS2; + setup->fout = rate; + ret = 0; + } + break; + case PLL_BYPASS1: + ret = clk_sscg_pll2_find_setup(setup, &temp_setup, prate); + break; + case PLL_BYPASS_NONE: + ret = clk_sscg_pll1_find_setup(setup, &temp_setup, prate); + break; + } + + return ret; +} + +static int clk_sscg_pll_is_prepared(struct clk_hw *hw) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + + u32 val = readl_relaxed(pll->base + PLL_CFG0); + + return (val & PLL_PD_MASK) ? 0 : 1; +} + +static int clk_sscg_pll_prepare(struct clk_hw *hw) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + u32 val; + + val = readl_relaxed(pll->base + PLL_CFG0); + val &= ~PLL_PD_MASK; + writel_relaxed(val, pll->base + PLL_CFG0); + + return clk_sscg_pll_wait_lock(pll); +} + +static void clk_sscg_pll_unprepare(struct clk_hw *hw) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + u32 val; + + val = readl_relaxed(pll->base + PLL_CFG0); + val |= PLL_PD_MASK; + writel_relaxed(val, pll->base + PLL_CFG0); +} + +static unsigned long clk_sscg_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + u32 val, divr1, divf1, divr2, divf2, divq; + u64 temp64; + + val = readl_relaxed(pll->base + PLL_CFG2); + divr1 = FIELD_GET(PLL_DIVR1_MASK, val); + divr2 = FIELD_GET(PLL_DIVR2_MASK, val); + divf1 = FIELD_GET(PLL_DIVF1_MASK, val); + divf2 = FIELD_GET(PLL_DIVF2_MASK, val); + divq = FIELD_GET(PLL_DIVQ_MASK, val); + + temp64 = parent_rate; + + val = readl(pll->base + PLL_CFG0); + if (val & SSCG_PLL_BYPASS2_MASK) { + temp64 = parent_rate; + } else if (val & SSCG_PLL_BYPASS1_MASK) { + temp64 *= divf2; + do_div(temp64, (divr2 + 1) * (divq + 1)); + } else { + temp64 *= 2; + temp64 *= (divf1 + 1) * (divf2 + 1); + do_div(temp64, (divr1 + 1) * (divr2 + 1) * (divq + 1)); + } + + return temp64; +} + +static int clk_sscg_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + struct clk_sscg_pll_setup *setup = &pll->setup; + u32 val; + + /* set bypass here too since the parent might be the same */ + val = readl(pll->base + PLL_CFG0); + val &= ~SSCG_PLL_BYPASS_MASK; + val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, setup->bypass); + writel(val, pll->base + PLL_CFG0); + + val = readl_relaxed(pll->base + PLL_CFG2); + val &= ~(PLL_DIVF1_MASK | PLL_DIVF2_MASK); + val &= ~(PLL_DIVR1_MASK | PLL_DIVR2_MASK | PLL_DIVQ_MASK); + val |= FIELD_PREP(PLL_DIVF1_MASK, setup->divf1); + val |= FIELD_PREP(PLL_DIVF2_MASK, setup->divf2); + val |= FIELD_PREP(PLL_DIVR1_MASK, setup->divr1); + val |= FIELD_PREP(PLL_DIVR2_MASK, setup->divr2); + val |= FIELD_PREP(PLL_DIVQ_MASK, setup->divq); + writel_relaxed(val, pll->base + PLL_CFG2); + + return clk_sscg_pll_wait_lock(pll); +} + +static u8 clk_sscg_pll_get_parent(struct clk_hw *hw) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + u32 val; + u8 ret = pll->parent; + + val = readl(pll->base + PLL_CFG0); + if (val & SSCG_PLL_BYPASS2_MASK) + ret = pll->bypass2; + else if (val & SSCG_PLL_BYPASS1_MASK) + ret = pll->bypass1; + return ret; +} + +static int clk_sscg_pll_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + u32 val; + + val = readl(pll->base + PLL_CFG0); + val &= ~SSCG_PLL_BYPASS_MASK; + val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, pll->setup.bypass); + writel(val, pll->base + PLL_CFG0); + + return clk_sscg_pll_wait_lock(pll); +} + +static int __clk_sscg_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req, + uint64_t min, + uint64_t max, + uint64_t rate, + int bypass) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + struct clk_sscg_pll_setup *setup = &pll->setup; + struct clk_hw *parent_hw = NULL; + int bypass_parent_index; + int ret; + + req->max_rate = max; + req->min_rate = min; + + switch (bypass) { + case PLL_BYPASS2: + bypass_parent_index = pll->bypass2; + break; + case PLL_BYPASS1: + bypass_parent_index = pll->bypass1; + break; + default: + bypass_parent_index = pll->parent; + break; + } + + parent_hw = clk_hw_get_parent_by_index(hw, bypass_parent_index); + ret = __clk_determine_rate(parent_hw, req); + if (!ret) { + ret = clk_sscg_pll_find_setup(setup, req->rate, + rate, bypass); + } + + req->best_parent_hw = parent_hw; + req->best_parent_rate = req->rate; + req->rate = setup->fout; + + return ret; +} + +static int clk_sscg_pll_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_sscg_pll *pll = to_clk_sscg_pll(hw); + struct clk_sscg_pll_setup *setup = &pll->setup; + uint64_t rate = req->rate; + uint64_t min = req->min_rate; + uint64_t max = req->max_rate; + int ret; + + if (rate < PLL_OUT_MIN_FREQ || rate > PLL_OUT_MAX_FREQ) + return -EINVAL; + + ret = __clk_sscg_pll_determine_rate(hw, req, req->rate, req->rate, + rate, PLL_BYPASS2); + if (!ret) + return ret; + + ret = __clk_sscg_pll_determine_rate(hw, req, PLL_STAGE1_REF_MIN_FREQ, + PLL_STAGE1_REF_MAX_FREQ, rate, + PLL_BYPASS1); + if (!ret) + return ret; + + ret = __clk_sscg_pll_determine_rate(hw, req, PLL_REF_MIN_FREQ, + PLL_REF_MAX_FREQ, rate, + PLL_BYPASS_NONE); + if (!ret) + return ret; + + if (setup->fout >= min && setup->fout <= max) + ret = 0; + + return ret; +} + +static const struct clk_ops clk_sscg_pll_ops = { + .prepare = clk_sscg_pll_prepare, + .unprepare = clk_sscg_pll_unprepare, + .is_prepared = clk_sscg_pll_is_prepared, + .recalc_rate = clk_sscg_pll_recalc_rate, + .set_rate = clk_sscg_pll_set_rate, + .set_parent = clk_sscg_pll_set_parent, + .get_parent = clk_sscg_pll_get_parent, + .determine_rate = clk_sscg_pll_determine_rate, +}; + +struct clk_hw *imx_clk_hw_sscg_pll(const char *name, + const char * const *parent_names, + u8 num_parents, + u8 parent, u8 bypass1, u8 bypass2, + void __iomem *base, + unsigned long flags) +{ + struct clk_sscg_pll *pll; + struct clk_init_data init; + struct clk_hw *hw; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->parent = parent; + pll->bypass1 = bypass1; + pll->bypass2 = bypass2; + + pll->base = base; + init.name = name; + init.ops = &clk_sscg_pll_ops; + + init.flags = flags; + init.parent_names = parent_names; + init.num_parents = num_parents; + + pll->hw.init = &init; + + hw = &pll->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + return ERR_PTR(ret); + } + + return hw; +} +EXPORT_SYMBOL_GPL(imx_clk_hw_sscg_pll); diff --git a/drivers/clk/imx/clk-vf610.c b/drivers/clk/imx/clk-vf610.c new file mode 100644 index 000000000..9e11f1c7c --- /dev/null +++ b/drivers/clk/imx/clk-vf610.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2012-2013 Freescale Semiconductor, Inc. + */ + +#include <linux/of_address.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/syscore_ops.h> +#include <dt-bindings/clock/vf610-clock.h> + +#include "clk.h" + +#define CCM_CCR (ccm_base + 0x00) +#define CCM_CSR (ccm_base + 0x04) +#define CCM_CCSR (ccm_base + 0x08) +#define CCM_CACRR (ccm_base + 0x0c) +#define CCM_CSCMR1 (ccm_base + 0x10) +#define CCM_CSCDR1 (ccm_base + 0x14) +#define CCM_CSCDR2 (ccm_base + 0x18) +#define CCM_CSCDR3 (ccm_base + 0x1c) +#define CCM_CSCMR2 (ccm_base + 0x20) +#define CCM_CSCDR4 (ccm_base + 0x24) +#define CCM_CLPCR (ccm_base + 0x2c) +#define CCM_CISR (ccm_base + 0x30) +#define CCM_CIMR (ccm_base + 0x34) +#define CCM_CGPR (ccm_base + 0x3c) +#define CCM_CCGR0 (ccm_base + 0x40) +#define CCM_CCGR1 (ccm_base + 0x44) +#define CCM_CCGR2 (ccm_base + 0x48) +#define CCM_CCGR3 (ccm_base + 0x4c) +#define CCM_CCGR4 (ccm_base + 0x50) +#define CCM_CCGR5 (ccm_base + 0x54) +#define CCM_CCGR6 (ccm_base + 0x58) +#define CCM_CCGR7 (ccm_base + 0x5c) +#define CCM_CCGR8 (ccm_base + 0x60) +#define CCM_CCGR9 (ccm_base + 0x64) +#define CCM_CCGR10 (ccm_base + 0x68) +#define CCM_CCGR11 (ccm_base + 0x6c) +#define CCM_CCGRx(x) (CCM_CCGR0 + (x) * 4) +#define CCM_CMEOR0 (ccm_base + 0x70) +#define CCM_CMEOR1 (ccm_base + 0x74) +#define CCM_CMEOR2 (ccm_base + 0x78) +#define CCM_CMEOR3 (ccm_base + 0x7c) +#define CCM_CMEOR4 (ccm_base + 0x80) +#define CCM_CMEOR5 (ccm_base + 0x84) +#define CCM_CPPDSR (ccm_base + 0x88) +#define CCM_CCOWR (ccm_base + 0x8c) +#define CCM_CCPGR0 (ccm_base + 0x90) +#define CCM_CCPGR1 (ccm_base + 0x94) +#define CCM_CCPGR2 (ccm_base + 0x98) +#define CCM_CCPGR3 (ccm_base + 0x9c) + +#define CCM_CCGRx_CGn(n) ((n) * 2) + +#define PFD_PLL1_BASE (anatop_base + 0x2b0) +#define PFD_PLL2_BASE (anatop_base + 0x100) +#define PFD_PLL3_BASE (anatop_base + 0xf0) +#define PLL1_CTRL (anatop_base + 0x270) +#define PLL2_CTRL (anatop_base + 0x30) +#define PLL3_CTRL (anatop_base + 0x10) +#define PLL4_CTRL (anatop_base + 0x70) +#define PLL5_CTRL (anatop_base + 0xe0) +#define PLL6_CTRL (anatop_base + 0xa0) +#define PLL7_CTRL (anatop_base + 0x20) +#define ANA_MISC1 (anatop_base + 0x160) + +static void __iomem *anatop_base; +static void __iomem *ccm_base; + +/* sources for multiplexer clocks, this is used multiple times */ +static const char *fast_sels[] = { "firc", "fxosc", }; +static const char *slow_sels[] = { "sirc_32k", "sxosc", }; +static const char *pll1_sels[] = { "pll1_sys", "pll1_pfd1", "pll1_pfd2", "pll1_pfd3", "pll1_pfd4", }; +static const char *pll2_sels[] = { "pll2_bus", "pll2_pfd1", "pll2_pfd2", "pll2_pfd3", "pll2_pfd4", }; +static const char *pll_bypass_src_sels[] = { "fast_clk_sel", "lvds1_in", }; +static const char *pll1_bypass_sels[] = { "pll1", "pll1_bypass_src", }; +static const char *pll2_bypass_sels[] = { "pll2", "pll2_bypass_src", }; +static const char *pll3_bypass_sels[] = { "pll3", "pll3_bypass_src", }; +static const char *pll4_bypass_sels[] = { "pll4", "pll4_bypass_src", }; +static const char *pll5_bypass_sels[] = { "pll5", "pll5_bypass_src", }; +static const char *pll6_bypass_sels[] = { "pll6", "pll6_bypass_src", }; +static const char *pll7_bypass_sels[] = { "pll7", "pll7_bypass_src", }; +static const char *sys_sels[] = { "fast_clk_sel", "slow_clk_sel", "pll2_pfd_sel", "pll2_bus", "pll1_pfd_sel", "pll3_usb_otg", }; +static const char *ddr_sels[] = { "pll2_pfd2", "sys_sel", }; +static const char *rmii_sels[] = { "enet_ext", "audio_ext", "enet_50m", "enet_25m", }; +static const char *enet_ts_sels[] = { "enet_ext", "fxosc", "audio_ext", "usb", "enet_ts", "enet_25m", "enet_50m", }; +static const char *esai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", }; +static const char *sai_sels[] = { "audio_ext", "mlb", "spdif_rx", "pll4_audio_div", }; +static const char *nfc_sels[] = { "platform_bus", "pll1_pfd1", "pll3_pfd1", "pll3_pfd3", }; +static const char *qspi_sels[] = { "pll3_usb_otg", "pll3_pfd4", "pll2_pfd4", "pll1_pfd4", }; +static const char *esdhc_sels[] = { "pll3_usb_otg", "pll3_pfd3", "pll1_pfd3", "platform_bus", }; +static const char *dcu_sels[] = { "pll1_pfd2", "pll3_usb_otg", }; +static const char *gpu_sels[] = { "pll2_pfd2", "pll3_pfd2", }; +static const char *vadc_sels[] = { "pll6_video_div", "pll3_usb_otg_div", "pll3_usb_otg", }; +/* FTM counter clock source, not module clock */ +static const char *ftm_ext_sels[] = {"sirc_128k", "sxosc", "fxosc_half", "audio_ext", }; +static const char *ftm_fix_sels[] = { "sxosc", "ipg_bus", }; + + +static const struct clk_div_table pll4_audio_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 6 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 10 }, + { .val = 5, .div = 12 }, + { .val = 6, .div = 14 }, + { .val = 7, .div = 16 }, + { } +}; + +static struct clk *clk[VF610_CLK_END]; +static struct clk_onecell_data clk_data; + +static u32 cscmr1; +static u32 cscmr2; +static u32 cscdr1; +static u32 cscdr2; +static u32 cscdr3; +static u32 ccgr[12]; + +static unsigned int const clks_init_on[] __initconst = { + VF610_CLK_SYS_BUS, + VF610_CLK_DDR_SEL, + VF610_CLK_DAP, + VF610_CLK_DDRMC, + VF610_CLK_WKPU, +}; + +static struct clk * __init vf610_get_fixed_clock( + struct device_node *ccm_node, const char *name) +{ + struct clk *clk = of_clk_get_by_name(ccm_node, name); + + /* Backward compatibility if device tree is missing clks assignments */ + if (IS_ERR(clk)) + clk = imx_obtain_fixed_clock(name, 0); + return clk; +}; + +static int vf610_clk_suspend(void) +{ + int i; + + cscmr1 = readl_relaxed(CCM_CSCMR1); + cscmr2 = readl_relaxed(CCM_CSCMR2); + + cscdr1 = readl_relaxed(CCM_CSCDR1); + cscdr2 = readl_relaxed(CCM_CSCDR2); + cscdr3 = readl_relaxed(CCM_CSCDR3); + + for (i = 0; i < 12; i++) + ccgr[i] = readl_relaxed(CCM_CCGRx(i)); + + return 0; +} + +static void vf610_clk_resume(void) +{ + int i; + + writel_relaxed(cscmr1, CCM_CSCMR1); + writel_relaxed(cscmr2, CCM_CSCMR2); + + writel_relaxed(cscdr1, CCM_CSCDR1); + writel_relaxed(cscdr2, CCM_CSCDR2); + writel_relaxed(cscdr3, CCM_CSCDR3); + + for (i = 0; i < 12; i++) + writel_relaxed(ccgr[i], CCM_CCGRx(i)); +} + +static struct syscore_ops vf610_clk_syscore_ops = { + .suspend = vf610_clk_suspend, + .resume = vf610_clk_resume, +}; + +static void __init vf610_clocks_init(struct device_node *ccm_node) +{ + struct device_node *np; + int i; + + clk[VF610_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clk[VF610_CLK_SIRC_128K] = imx_clk_fixed("sirc_128k", 128000); + clk[VF610_CLK_SIRC_32K] = imx_clk_fixed("sirc_32k", 32000); + clk[VF610_CLK_FIRC] = imx_clk_fixed("firc", 24000000); + + clk[VF610_CLK_SXOSC] = vf610_get_fixed_clock(ccm_node, "sxosc"); + clk[VF610_CLK_FXOSC] = vf610_get_fixed_clock(ccm_node, "fxosc"); + clk[VF610_CLK_AUDIO_EXT] = vf610_get_fixed_clock(ccm_node, "audio_ext"); + clk[VF610_CLK_ENET_EXT] = vf610_get_fixed_clock(ccm_node, "enet_ext"); + + /* Clock source from external clock via LVDs PAD */ + clk[VF610_CLK_ANACLK1] = vf610_get_fixed_clock(ccm_node, "anaclk1"); + + clk[VF610_CLK_FXOSC_HALF] = imx_clk_fixed_factor("fxosc_half", "fxosc", 1, 2); + + np = of_find_compatible_node(NULL, NULL, "fsl,vf610-anatop"); + anatop_base = of_iomap(np, 0); + BUG_ON(!anatop_base); + of_node_put(np); + + np = ccm_node; + ccm_base = of_iomap(np, 0); + BUG_ON(!ccm_base); + + clk[VF610_CLK_SLOW_CLK_SEL] = imx_clk_mux("slow_clk_sel", CCM_CCSR, 4, 1, slow_sels, ARRAY_SIZE(slow_sels)); + clk[VF610_CLK_FASK_CLK_SEL] = imx_clk_mux("fast_clk_sel", CCM_CCSR, 5, 1, fast_sels, ARRAY_SIZE(fast_sels)); + + clk[VF610_CLK_PLL1_BYPASS_SRC] = imx_clk_mux("pll1_bypass_src", PLL1_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL2_BYPASS_SRC] = imx_clk_mux("pll2_bypass_src", PLL2_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL3_BYPASS_SRC] = imx_clk_mux("pll3_bypass_src", PLL3_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL4_BYPASS_SRC] = imx_clk_mux("pll4_bypass_src", PLL4_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL5_BYPASS_SRC] = imx_clk_mux("pll5_bypass_src", PLL5_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL6_BYPASS_SRC] = imx_clk_mux("pll6_bypass_src", PLL6_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + clk[VF610_CLK_PLL7_BYPASS_SRC] = imx_clk_mux("pll7_bypass_src", PLL7_CTRL, 14, 1, pll_bypass_src_sels, ARRAY_SIZE(pll_bypass_src_sels)); + + clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1); + clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_SYS_VF610, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1); + clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x2); + clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f); + clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3); + clk[VF610_CLK_PLL6] = imx_clk_pllv3(IMX_PLLV3_AV, "pll6", "pll6_bypass_src", PLL6_CTRL, 0x7f); + clk[VF610_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll7", "pll7_bypass_src", PLL7_CTRL, 0x2); + + clk[VF610_PLL1_BYPASS] = imx_clk_mux_flags("pll1_bypass", PLL1_CTRL, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL2_BYPASS] = imx_clk_mux_flags("pll2_bypass", PLL2_CTRL, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL3_BYPASS] = imx_clk_mux_flags("pll3_bypass", PLL3_CTRL, 16, 1, pll3_bypass_sels, ARRAY_SIZE(pll3_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL4_BYPASS] = imx_clk_mux_flags("pll4_bypass", PLL4_CTRL, 16, 1, pll4_bypass_sels, ARRAY_SIZE(pll4_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL5_BYPASS] = imx_clk_mux_flags("pll5_bypass", PLL5_CTRL, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL6_BYPASS] = imx_clk_mux_flags("pll6_bypass", PLL6_CTRL, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT); + clk[VF610_PLL7_BYPASS] = imx_clk_mux_flags("pll7_bypass", PLL7_CTRL, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT); + + /* Do not bypass PLLs initially */ + clk_set_parent(clk[VF610_PLL1_BYPASS], clk[VF610_CLK_PLL1]); + clk_set_parent(clk[VF610_PLL2_BYPASS], clk[VF610_CLK_PLL2]); + clk_set_parent(clk[VF610_PLL3_BYPASS], clk[VF610_CLK_PLL3]); + clk_set_parent(clk[VF610_PLL4_BYPASS], clk[VF610_CLK_PLL4]); + clk_set_parent(clk[VF610_PLL5_BYPASS], clk[VF610_CLK_PLL5]); + clk_set_parent(clk[VF610_PLL6_BYPASS], clk[VF610_CLK_PLL6]); + clk_set_parent(clk[VF610_PLL7_BYPASS], clk[VF610_CLK_PLL7]); + + clk[VF610_CLK_PLL1_SYS] = imx_clk_gate("pll1_sys", "pll1_bypass", PLL1_CTRL, 13); + clk[VF610_CLK_PLL2_BUS] = imx_clk_gate("pll2_bus", "pll2_bypass", PLL2_CTRL, 13); + clk[VF610_CLK_PLL3_USB_OTG] = imx_clk_gate("pll3_usb_otg", "pll3_bypass", PLL3_CTRL, 13); + clk[VF610_CLK_PLL4_AUDIO] = imx_clk_gate("pll4_audio", "pll4_bypass", PLL4_CTRL, 13); + clk[VF610_CLK_PLL5_ENET] = imx_clk_gate("pll5_enet", "pll5_bypass", PLL5_CTRL, 13); + clk[VF610_CLK_PLL6_VIDEO] = imx_clk_gate("pll6_video", "pll6_bypass", PLL6_CTRL, 13); + clk[VF610_CLK_PLL7_USB_HOST] = imx_clk_gate("pll7_usb_host", "pll7_bypass", PLL7_CTRL, 13); + + clk[VF610_CLK_LVDS1_IN] = imx_clk_gate_exclusive("lvds1_in", "anaclk1", ANA_MISC1, 12, BIT(10)); + + clk[VF610_CLK_PLL1_PFD1] = imx_clk_pfd("pll1_pfd1", "pll1_sys", PFD_PLL1_BASE, 0); + clk[VF610_CLK_PLL1_PFD2] = imx_clk_pfd("pll1_pfd2", "pll1_sys", PFD_PLL1_BASE, 1); + clk[VF610_CLK_PLL1_PFD3] = imx_clk_pfd("pll1_pfd3", "pll1_sys", PFD_PLL1_BASE, 2); + clk[VF610_CLK_PLL1_PFD4] = imx_clk_pfd("pll1_pfd4", "pll1_sys", PFD_PLL1_BASE, 3); + + clk[VF610_CLK_PLL2_PFD1] = imx_clk_pfd("pll2_pfd1", "pll2_bus", PFD_PLL2_BASE, 0); + clk[VF610_CLK_PLL2_PFD2] = imx_clk_pfd("pll2_pfd2", "pll2_bus", PFD_PLL2_BASE, 1); + clk[VF610_CLK_PLL2_PFD3] = imx_clk_pfd("pll2_pfd3", "pll2_bus", PFD_PLL2_BASE, 2); + clk[VF610_CLK_PLL2_PFD4] = imx_clk_pfd("pll2_pfd4", "pll2_bus", PFD_PLL2_BASE, 3); + + clk[VF610_CLK_PLL3_PFD1] = imx_clk_pfd("pll3_pfd1", "pll3_usb_otg", PFD_PLL3_BASE, 0); + clk[VF610_CLK_PLL3_PFD2] = imx_clk_pfd("pll3_pfd2", "pll3_usb_otg", PFD_PLL3_BASE, 1); + clk[VF610_CLK_PLL3_PFD3] = imx_clk_pfd("pll3_pfd3", "pll3_usb_otg", PFD_PLL3_BASE, 2); + clk[VF610_CLK_PLL3_PFD4] = imx_clk_pfd("pll3_pfd4", "pll3_usb_otg", PFD_PLL3_BASE, 3); + + clk[VF610_CLK_PLL1_PFD_SEL] = imx_clk_mux("pll1_pfd_sel", CCM_CCSR, 16, 3, pll1_sels, 5); + clk[VF610_CLK_PLL2_PFD_SEL] = imx_clk_mux("pll2_pfd_sel", CCM_CCSR, 19, 3, pll2_sels, 5); + clk[VF610_CLK_SYS_SEL] = imx_clk_mux("sys_sel", CCM_CCSR, 0, 3, sys_sels, ARRAY_SIZE(sys_sels)); + clk[VF610_CLK_DDR_SEL] = imx_clk_mux("ddr_sel", CCM_CCSR, 6, 1, ddr_sels, ARRAY_SIZE(ddr_sels)); + clk[VF610_CLK_SYS_BUS] = imx_clk_divider("sys_bus", "sys_sel", CCM_CACRR, 0, 3); + clk[VF610_CLK_PLATFORM_BUS] = imx_clk_divider("platform_bus", "sys_bus", CCM_CACRR, 3, 3); + clk[VF610_CLK_IPG_BUS] = imx_clk_divider("ipg_bus", "platform_bus", CCM_CACRR, 11, 2); + + clk[VF610_CLK_PLL3_MAIN_DIV] = imx_clk_divider("pll3_usb_otg_div", "pll3_usb_otg", CCM_CACRR, 20, 1); + clk[VF610_CLK_PLL4_MAIN_DIV] = clk_register_divider_table(NULL, "pll4_audio_div", "pll4_audio", 0, CCM_CACRR, 6, 3, 0, pll4_audio_div_table, &imx_ccm_lock); + clk[VF610_CLK_PLL6_MAIN_DIV] = imx_clk_divider("pll6_video_div", "pll6_video", CCM_CACRR, 21, 1); + + clk[VF610_CLK_DDRMC] = imx_clk_gate2_cgr("ddrmc", "ddr_sel", CCM_CCGR6, CCM_CCGRx_CGn(14), 0x2); + clk[VF610_CLK_WKPU] = imx_clk_gate2_cgr("wkpu", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(10), 0x2); + + clk[VF610_CLK_USBPHY0] = imx_clk_gate("usbphy0", "pll3_usb_otg", PLL3_CTRL, 6); + clk[VF610_CLK_USBPHY1] = imx_clk_gate("usbphy1", "pll7_usb_host", PLL7_CTRL, 6); + + clk[VF610_CLK_USBC0] = imx_clk_gate2("usbc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(4)); + clk[VF610_CLK_USBC1] = imx_clk_gate2("usbc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI0_SEL] = imx_clk_mux("qspi0_sel", CCM_CSCMR1, 22, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI0_EN] = imx_clk_gate("qspi0_en", "qspi0_sel", CCM_CSCDR3, 4); + clk[VF610_CLK_QSPI0_X4_DIV] = imx_clk_divider("qspi0_x4", "qspi0_en", CCM_CSCDR3, 0, 2); + clk[VF610_CLK_QSPI0_X2_DIV] = imx_clk_divider("qspi0_x2", "qspi0_x4", CCM_CSCDR3, 2, 1); + clk[VF610_CLK_QSPI0_X1_DIV] = imx_clk_divider("qspi0_x1", "qspi0_x2", CCM_CSCDR3, 3, 1); + clk[VF610_CLK_QSPI0] = imx_clk_gate2("qspi0", "qspi0_x1", CCM_CCGR2, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_QSPI1_SEL] = imx_clk_mux("qspi1_sel", CCM_CSCMR1, 24, 2, qspi_sels, 4); + clk[VF610_CLK_QSPI1_EN] = imx_clk_gate("qspi1_en", "qspi1_sel", CCM_CSCDR3, 12); + clk[VF610_CLK_QSPI1_X4_DIV] = imx_clk_divider("qspi1_x4", "qspi1_en", CCM_CSCDR3, 8, 2); + clk[VF610_CLK_QSPI1_X2_DIV] = imx_clk_divider("qspi1_x2", "qspi1_x4", CCM_CSCDR3, 10, 1); + clk[VF610_CLK_QSPI1_X1_DIV] = imx_clk_divider("qspi1_x1", "qspi1_x2", CCM_CSCDR3, 11, 1); + clk[VF610_CLK_QSPI1] = imx_clk_gate2("qspi1", "qspi1_x1", CCM_CCGR8, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_ENET_50M] = imx_clk_fixed_factor("enet_50m", "pll5_enet", 1, 10); + clk[VF610_CLK_ENET_25M] = imx_clk_fixed_factor("enet_25m", "pll5_enet", 1, 20); + clk[VF610_CLK_ENET_SEL] = imx_clk_mux("enet_sel", CCM_CSCMR2, 4, 2, rmii_sels, 4); + clk[VF610_CLK_ENET_TS_SEL] = imx_clk_mux("enet_ts_sel", CCM_CSCMR2, 0, 3, enet_ts_sels, 7); + clk[VF610_CLK_ENET] = imx_clk_gate("enet", "enet_sel", CCM_CSCDR1, 24); + clk[VF610_CLK_ENET_TS] = imx_clk_gate("enet_ts", "enet_ts_sel", CCM_CSCDR1, 23); + clk[VF610_CLK_ENET0] = imx_clk_gate2("enet0", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_ENET1] = imx_clk_gate2("enet1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_UART0] = imx_clk_gate2_cgr("uart0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(7), 0x2); + clk[VF610_CLK_UART1] = imx_clk_gate2_cgr("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8), 0x2); + clk[VF610_CLK_UART2] = imx_clk_gate2_cgr("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9), 0x2); + clk[VF610_CLK_UART3] = imx_clk_gate2_cgr("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10), 0x2); + clk[VF610_CLK_UART4] = imx_clk_gate2_cgr("uart4", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(9), 0x2); + clk[VF610_CLK_UART5] = imx_clk_gate2_cgr("uart5", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(10), 0x2); + + clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(6)); + clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(7)); + clk[VF610_CLK_I2C2] = imx_clk_gate2("i2c2", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(6)); + clk[VF610_CLK_I2C3] = imx_clk_gate2("i2c3", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_DSPI0] = imx_clk_gate2("dspi0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI1] = imx_clk_gate2("dspi1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_DSPI2] = imx_clk_gate2("dspi2", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DSPI3] = imx_clk_gate2("dspi3", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_CRC] = imx_clk_gate2("crc", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(3)); + clk[VF610_CLK_WDT] = imx_clk_gate2("wdt", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(14)); + + clk[VF610_CLK_ESDHC0_SEL] = imx_clk_mux("esdhc0_sel", CCM_CSCMR1, 16, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC0_EN] = imx_clk_gate("esdhc0_en", "esdhc0_sel", CCM_CSCDR2, 28); + clk[VF610_CLK_ESDHC0_DIV] = imx_clk_divider("esdhc0_div", "esdhc0_en", CCM_CSCDR2, 16, 4); + clk[VF610_CLK_ESDHC0] = imx_clk_gate2("eshc0", "esdhc0_div", CCM_CCGR7, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_ESDHC1_SEL] = imx_clk_mux("esdhc1_sel", CCM_CSCMR1, 18, 2, esdhc_sels, 4); + clk[VF610_CLK_ESDHC1_EN] = imx_clk_gate("esdhc1_en", "esdhc1_sel", CCM_CSCDR2, 29); + clk[VF610_CLK_ESDHC1_DIV] = imx_clk_divider("esdhc1_div", "esdhc1_en", CCM_CSCDR2, 20, 4); + clk[VF610_CLK_ESDHC1] = imx_clk_gate2("eshc1", "esdhc1_div", CCM_CCGR7, CCM_CCGRx_CGn(2)); + + /* + * ftm_ext_clk and ftm_fix_clk are FTM timer counter's + * selectable clock sources, both use a common enable bit + * in CCM_CSCDR1, selecting "dummy" clock as parent of + * "ftm0_ext_fix" make it serve only for enable/disable. + */ + clk[VF610_CLK_FTM0_EXT_SEL] = imx_clk_mux("ftm0_ext_sel", CCM_CSCMR2, 6, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM0_FIX_SEL] = imx_clk_mux("ftm0_fix_sel", CCM_CSCMR2, 14, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM0_EXT_FIX_EN] = imx_clk_gate("ftm0_ext_fix_en", "dummy", CCM_CSCDR1, 25); + clk[VF610_CLK_FTM1_EXT_SEL] = imx_clk_mux("ftm1_ext_sel", CCM_CSCMR2, 8, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM1_FIX_SEL] = imx_clk_mux("ftm1_fix_sel", CCM_CSCMR2, 15, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM1_EXT_FIX_EN] = imx_clk_gate("ftm1_ext_fix_en", "dummy", CCM_CSCDR1, 26); + clk[VF610_CLK_FTM2_EXT_SEL] = imx_clk_mux("ftm2_ext_sel", CCM_CSCMR2, 10, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM2_FIX_SEL] = imx_clk_mux("ftm2_fix_sel", CCM_CSCMR2, 16, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM2_EXT_FIX_EN] = imx_clk_gate("ftm2_ext_fix_en", "dummy", CCM_CSCDR1, 27); + clk[VF610_CLK_FTM3_EXT_SEL] = imx_clk_mux("ftm3_ext_sel", CCM_CSCMR2, 12, 2, ftm_ext_sels, 4); + clk[VF610_CLK_FTM3_FIX_SEL] = imx_clk_mux("ftm3_fix_sel", CCM_CSCMR2, 17, 1, ftm_fix_sels, 2); + clk[VF610_CLK_FTM3_EXT_FIX_EN] = imx_clk_gate("ftm3_ext_fix_en", "dummy", CCM_CSCDR1, 28); + + /* ftm(n)_clk are FTM module operation clock */ + clk[VF610_CLK_FTM0] = imx_clk_gate2("ftm0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM1] = imx_clk_gate2("ftm1", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(9)); + clk[VF610_CLK_FTM2] = imx_clk_gate2("ftm2", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_FTM3] = imx_clk_gate2("ftm3", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(9)); + + clk[VF610_CLK_DCU0_SEL] = imx_clk_mux("dcu0_sel", CCM_CSCMR1, 28, 1, dcu_sels, 2); + clk[VF610_CLK_DCU0_EN] = imx_clk_gate("dcu0_en", "dcu0_sel", CCM_CSCDR3, 19); + clk[VF610_CLK_DCU0_DIV] = imx_clk_divider("dcu0_div", "dcu0_en", CCM_CSCDR3, 16, 3); + clk[VF610_CLK_DCU0] = imx_clk_gate2("dcu0", "ipg_bus", CCM_CCGR3, CCM_CCGRx_CGn(8)); + clk[VF610_CLK_DCU1_SEL] = imx_clk_mux("dcu1_sel", CCM_CSCMR1, 29, 1, dcu_sels, 2); + clk[VF610_CLK_DCU1_EN] = imx_clk_gate("dcu1_en", "dcu1_sel", CCM_CSCDR3, 23); + clk[VF610_CLK_DCU1_DIV] = imx_clk_divider("dcu1_div", "dcu1_en", CCM_CSCDR3, 20, 3); + clk[VF610_CLK_DCU1] = imx_clk_gate2("dcu1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(8)); + + clk[VF610_CLK_TCON0] = imx_clk_gate2("tcon0", "platform_bus", CCM_CCGR1, CCM_CCGRx_CGn(13)); + clk[VF610_CLK_TCON1] = imx_clk_gate2("tcon1", "platform_bus", CCM_CCGR7, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_ESAI_SEL] = imx_clk_mux("esai_sel", CCM_CSCMR1, 20, 2, esai_sels, 4); + clk[VF610_CLK_ESAI_EN] = imx_clk_gate("esai_en", "esai_sel", CCM_CSCDR2, 30); + clk[VF610_CLK_ESAI_DIV] = imx_clk_divider("esai_div", "esai_en", CCM_CSCDR2, 24, 4); + clk[VF610_CLK_ESAI] = imx_clk_gate2("esai", "esai_div", CCM_CCGR4, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_SAI0_SEL] = imx_clk_mux("sai0_sel", CCM_CSCMR1, 0, 2, sai_sels, 4); + clk[VF610_CLK_SAI0_EN] = imx_clk_gate("sai0_en", "sai0_sel", CCM_CSCDR1, 16); + clk[VF610_CLK_SAI0_DIV] = imx_clk_divider("sai0_div", "sai0_en", CCM_CSCDR1, 0, 4); + clk[VF610_CLK_SAI0] = imx_clk_gate2("sai0", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_SAI1_SEL] = imx_clk_mux("sai1_sel", CCM_CSCMR1, 2, 2, sai_sels, 4); + clk[VF610_CLK_SAI1_EN] = imx_clk_gate("sai1_en", "sai1_sel", CCM_CSCDR1, 17); + clk[VF610_CLK_SAI1_DIV] = imx_clk_divider("sai1_div", "sai1_en", CCM_CSCDR1, 4, 4); + clk[VF610_CLK_SAI1] = imx_clk_gate2("sai1", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_SAI2_SEL] = imx_clk_mux("sai2_sel", CCM_CSCMR1, 4, 2, sai_sels, 4); + clk[VF610_CLK_SAI2_EN] = imx_clk_gate("sai2_en", "sai2_sel", CCM_CSCDR1, 18); + clk[VF610_CLK_SAI2_DIV] = imx_clk_divider("sai2_div", "sai2_en", CCM_CSCDR1, 8, 4); + clk[VF610_CLK_SAI2] = imx_clk_gate2("sai2", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_SAI3_SEL] = imx_clk_mux("sai3_sel", CCM_CSCMR1, 6, 2, sai_sels, 4); + clk[VF610_CLK_SAI3_EN] = imx_clk_gate("sai3_en", "sai3_sel", CCM_CSCDR1, 19); + clk[VF610_CLK_SAI3_DIV] = imx_clk_divider("sai3_div", "sai3_en", CCM_CSCDR1, 12, 4); + clk[VF610_CLK_SAI3] = imx_clk_gate2("sai3", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_NFC_SEL] = imx_clk_mux("nfc_sel", CCM_CSCMR1, 12, 2, nfc_sels, 4); + clk[VF610_CLK_NFC_EN] = imx_clk_gate("nfc_en", "nfc_sel", CCM_CSCDR2, 9); + clk[VF610_CLK_NFC_PRE_DIV] = imx_clk_divider("nfc_pre_div", "nfc_en", CCM_CSCDR3, 13, 3); + clk[VF610_CLK_NFC_FRAC_DIV] = imx_clk_divider("nfc_frac_div", "nfc_pre_div", CCM_CSCDR2, 4, 4); + clk[VF610_CLK_NFC] = imx_clk_gate2("nfc", "nfc_frac_div", CCM_CCGR10, CCM_CCGRx_CGn(0)); + + clk[VF610_CLK_GPU_SEL] = imx_clk_mux("gpu_sel", CCM_CSCMR1, 14, 1, gpu_sels, 2); + clk[VF610_CLK_GPU_EN] = imx_clk_gate("gpu_en", "gpu_sel", CCM_CSCDR2, 10); + clk[VF610_CLK_GPU2D] = imx_clk_gate2("gpu", "gpu_en", CCM_CCGR8, CCM_CCGRx_CGn(15)); + + clk[VF610_CLK_VADC_SEL] = imx_clk_mux("vadc_sel", CCM_CSCMR1, 8, 2, vadc_sels, 3); + clk[VF610_CLK_VADC_EN] = imx_clk_gate("vadc_en", "vadc_sel", CCM_CSCDR1, 22); + clk[VF610_CLK_VADC_DIV] = imx_clk_divider("vadc_div", "vadc_en", CCM_CSCDR1, 20, 2); + clk[VF610_CLK_VADC_DIV_HALF] = imx_clk_fixed_factor("vadc_div_half", "vadc_div", 1, 2); + clk[VF610_CLK_VADC] = imx_clk_gate2("vadc", "vadc_div", CCM_CCGR8, CCM_CCGRx_CGn(7)); + + clk[VF610_CLK_ADC0] = imx_clk_gate2("adc0", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_ADC1] = imx_clk_gate2("adc1", "ipg_bus", CCM_CCGR7, CCM_CCGRx_CGn(11)); + clk[VF610_CLK_DAC0] = imx_clk_gate2("dac0", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(12)); + clk[VF610_CLK_DAC1] = imx_clk_gate2("dac1", "ipg_bus", CCM_CCGR8, CCM_CCGRx_CGn(13)); + + clk[VF610_CLK_ASRC] = imx_clk_gate2("asrc", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(1)); + + clk[VF610_CLK_FLEXCAN0_EN] = imx_clk_gate("flexcan0_en", "ipg_bus", CCM_CSCDR2, 11); + clk[VF610_CLK_FLEXCAN0] = imx_clk_gate2("flexcan0", "flexcan0_en", CCM_CCGR0, CCM_CCGRx_CGn(0)); + clk[VF610_CLK_FLEXCAN1_EN] = imx_clk_gate("flexcan1_en", "ipg_bus", CCM_CSCDR2, 12); + clk[VF610_CLK_FLEXCAN1] = imx_clk_gate2("flexcan1", "flexcan1_en", CCM_CCGR9, CCM_CCGRx_CGn(4)); + + clk[VF610_CLK_DMAMUX0] = imx_clk_gate2("dmamux0", "platform_bus", CCM_CCGR0, CCM_CCGRx_CGn(4)); + clk[VF610_CLK_DMAMUX1] = imx_clk_gate2("dmamux1", "platform_bus", CCM_CCGR0, CCM_CCGRx_CGn(5)); + clk[VF610_CLK_DMAMUX2] = imx_clk_gate2("dmamux2", "platform_bus", CCM_CCGR6, CCM_CCGRx_CGn(1)); + clk[VF610_CLK_DMAMUX3] = imx_clk_gate2("dmamux3", "platform_bus", CCM_CCGR6, CCM_CCGRx_CGn(2)); + + clk[VF610_CLK_SNVS] = imx_clk_gate2("snvs-rtc", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(7)); + clk[VF610_CLK_DAP] = imx_clk_gate("dap", "platform_bus", CCM_CCSR, 24); + clk[VF610_CLK_OCOTP] = imx_clk_gate("ocotp", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(5)); + clk[VF610_CLK_CAAM] = imx_clk_gate2("caam", "ipg_bus", CCM_CCGR11, CCM_CCGRx_CGn(0)); + + imx_check_clocks(clk, ARRAY_SIZE(clk)); + + clk_set_parent(clk[VF610_CLK_QSPI0_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI0_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI0_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI0_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_QSPI1_SEL], clk[VF610_CLK_PLL1_PFD4]); + clk_set_rate(clk[VF610_CLK_QSPI1_X4_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_SEL]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X2_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X4_DIV]) / 2); + clk_set_rate(clk[VF610_CLK_QSPI1_X1_DIV], clk_get_rate(clk[VF610_CLK_QSPI1_X2_DIV]) / 2); + + clk_set_parent(clk[VF610_CLK_SAI0_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI1_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI2_SEL], clk[VF610_CLK_AUDIO_EXT]); + clk_set_parent(clk[VF610_CLK_SAI3_SEL], clk[VF610_CLK_AUDIO_EXT]); + + for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) + clk_prepare_enable(clk[clks_init_on[i]]); + + register_syscore_ops(&vf610_clk_syscore_ops); + + /* Add the clocks to provider list */ + clk_data.clks = clk; + clk_data.clk_num = ARRAY_SIZE(clk); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); +} +CLK_OF_DECLARE(vf610, "fsl,vf610-ccm", vf610_clocks_init); diff --git a/drivers/clk/imx/clk.c b/drivers/clk/imx/clk.c new file mode 100644 index 000000000..5582f18dd --- /dev/null +++ b/drivers/clk/imx/clk.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include "clk.h" + +#define CCM_CCDR 0x4 +#define CCDR_MMDC_CH0_MASK BIT(17) +#define CCDR_MMDC_CH1_MASK BIT(16) + +DEFINE_SPINLOCK(imx_ccm_lock); +EXPORT_SYMBOL_GPL(imx_ccm_lock); + +bool mcore_booted; +EXPORT_SYMBOL_GPL(mcore_booted); + +void imx_unregister_clocks(struct clk *clks[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) + clk_unregister(clks[i]); +} + +void imx_unregister_hw_clocks(struct clk_hw *hws[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) + clk_hw_unregister(hws[i]); +} +EXPORT_SYMBOL_GPL(imx_unregister_hw_clocks); + +void imx_mmdc_mask_handshake(void __iomem *ccm_base, + unsigned int chn) +{ + unsigned int reg; + + reg = readl_relaxed(ccm_base + CCM_CCDR); + reg |= chn == 0 ? CCDR_MMDC_CH0_MASK : CCDR_MMDC_CH1_MASK; + writel_relaxed(reg, ccm_base + CCM_CCDR); +} + +void imx_check_clocks(struct clk *clks[], unsigned int count) +{ + unsigned i; + + for (i = 0; i < count; i++) + if (IS_ERR(clks[i])) + pr_err("i.MX clk %u: register failed with %ld\n", + i, PTR_ERR(clks[i])); +} + +void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) + if (IS_ERR(clks[i])) + pr_err("i.MX clk %u: register failed with %ld\n", + i, PTR_ERR(clks[i])); +} +EXPORT_SYMBOL_GPL(imx_check_clk_hws); + +static struct clk *imx_obtain_fixed_clock_from_dt(const char *name) +{ + struct of_phandle_args phandle; + struct clk *clk = ERR_PTR(-ENODEV); + char *path; + + path = kasprintf(GFP_KERNEL, "/clocks/%s", name); + if (!path) + return ERR_PTR(-ENOMEM); + + phandle.np = of_find_node_by_path(path); + kfree(path); + + if (phandle.np) { + clk = of_clk_get_from_provider(&phandle); + of_node_put(phandle.np); + } + return clk; +} + +struct clk *imx_obtain_fixed_clock( + const char *name, unsigned long rate) +{ + struct clk *clk; + + clk = imx_obtain_fixed_clock_from_dt(name); + if (IS_ERR(clk)) + clk = imx_clk_fixed(name, rate); + return clk; +} + +struct clk_hw *imx_obtain_fixed_clock_hw( + const char *name, unsigned long rate) +{ + struct clk *clk; + + clk = imx_obtain_fixed_clock_from_dt(name); + if (IS_ERR(clk)) + clk = imx_clk_fixed(name, rate); + return __clk_get_hw(clk); +} + +struct clk_hw * imx_obtain_fixed_clk_hw(struct device_node *np, + const char *name) +{ + struct clk *clk; + + clk = of_clk_get_by_name(np, name); + if (IS_ERR(clk)) + return ERR_PTR(-ENOENT); + + return __clk_get_hw(clk); +} +EXPORT_SYMBOL_GPL(imx_obtain_fixed_clk_hw); + +/* + * This fixups the register CCM_CSCMR1 write value. + * The write/read/divider values of the aclk_podf field + * of that register have the relationship described by + * the following table: + * + * write value read value divider + * 3b'000 3b'110 7 + * 3b'001 3b'111 8 + * 3b'010 3b'100 5 + * 3b'011 3b'101 6 + * 3b'100 3b'010 3 + * 3b'101 3b'011 4 + * 3b'110 3b'000 1 + * 3b'111 3b'001 2(default) + * + * That's why we do the xor operation below. + */ +#define CSCMR1_FIXUP 0x00600000 + +void imx_cscmr1_fixup(u32 *val) +{ + *val ^= CSCMR1_FIXUP; + return; +} + +#ifndef MODULE + +static bool imx_keep_uart_clocks; +static int imx_enabled_uart_clocks; +static struct clk **imx_uart_clocks; + +static int __init imx_keep_uart_clocks_param(char *str) +{ + imx_keep_uart_clocks = 1; + + return 0; +} +__setup_param("earlycon", imx_keep_uart_earlycon, + imx_keep_uart_clocks_param, 0); +__setup_param("earlyprintk", imx_keep_uart_earlyprintk, + imx_keep_uart_clocks_param, 0); + +void imx_register_uart_clocks(unsigned int clk_count) +{ + imx_enabled_uart_clocks = 0; + +/* i.MX boards use device trees now. For build tests without CONFIG_OF, do nothing */ +#ifdef CONFIG_OF + if (imx_keep_uart_clocks) { + int i; + + imx_uart_clocks = kcalloc(clk_count, sizeof(struct clk *), GFP_KERNEL); + if (!imx_uart_clocks) + return; + + if (!of_stdout) + return; + + for (i = 0; i < clk_count; i++) { + imx_uart_clocks[imx_enabled_uart_clocks] = of_clk_get(of_stdout, i); + + /* Stop if there are no more of_stdout references */ + if (IS_ERR(imx_uart_clocks[imx_enabled_uart_clocks])) + return; + + /* Only enable the clock if it's not NULL */ + if (imx_uart_clocks[imx_enabled_uart_clocks]) + clk_prepare_enable(imx_uart_clocks[imx_enabled_uart_clocks++]); + } + } +#endif +} + +static int __init imx_clk_disable_uart(void) +{ + if (imx_keep_uart_clocks && imx_enabled_uart_clocks) { + int i; + + for (i = 0; i < imx_enabled_uart_clocks; i++) { + clk_disable_unprepare(imx_uart_clocks[i]); + clk_put(imx_uart_clocks[i]); + } + kfree(imx_uart_clocks); + } + + return 0; +} +late_initcall_sync(imx_clk_disable_uart); +#endif + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h new file mode 100644 index 000000000..fb5913139 --- /dev/null +++ b/drivers/clk/imx/clk.h @@ -0,0 +1,463 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __MACH_IMX_CLK_H +#define __MACH_IMX_CLK_H + +#include <linux/bits.h> +#include <linux/spinlock.h> +#include <linux/clk-provider.h> + +extern spinlock_t imx_ccm_lock; +extern bool mcore_booted; + +void imx_check_clocks(struct clk *clks[], unsigned int count); +void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count); +#ifndef MODULE +void imx_register_uart_clocks(unsigned int clk_count); +#else +static inline void imx_register_uart_clocks(unsigned int clk_count) +{ +} +#endif +void imx_mmdc_mask_handshake(void __iomem *ccm_base, unsigned int chn); +void imx_unregister_clocks(struct clk *clks[], unsigned int count); +void imx_unregister_hw_clocks(struct clk_hw *hws[], unsigned int count); + +extern void imx_cscmr1_fixup(u32 *val); + +enum imx_pllv1_type { + IMX_PLLV1_IMX1, + IMX_PLLV1_IMX21, + IMX_PLLV1_IMX25, + IMX_PLLV1_IMX27, + IMX_PLLV1_IMX31, + IMX_PLLV1_IMX35, +}; + +enum imx_sscg_pll_type { + SCCG_PLL1, + SCCG_PLL2, +}; + +enum imx_pll14xx_type { + PLL_1416X, + PLL_1443X, +}; + +enum imx_pllv4_type { + IMX_PLLV4_IMX7ULP, + IMX_PLLV4_IMX8ULP, + IMX_PLLV4_IMX8ULP_1GHZ, +}; + +enum imx_pfdv2_type { + IMX_PFDV2_IMX7ULP, + IMX_PFDV2_IMX8ULP, +}; + +/* NOTE: Rate table should be kept sorted in descending order. */ +struct imx_pll14xx_rate_table { + unsigned int rate; + unsigned int pdiv; + unsigned int mdiv; + unsigned int sdiv; + unsigned int kdiv; +}; + +struct imx_pll14xx_clk { + enum imx_pll14xx_type type; + const struct imx_pll14xx_rate_table *rate_table; + int rate_count; + int flags; +}; + +extern struct imx_pll14xx_clk imx_1416x_pll; +extern struct imx_pll14xx_clk imx_1443x_pll; +extern struct imx_pll14xx_clk imx_1443x_dram_pll; + +/* NOTE: Rate table should be kept sorted in descending order. */ +struct imx_fracn_gppll_rate_table { + unsigned int rate; + unsigned int mfi; + unsigned int mfn; + unsigned int mfd; + unsigned int rdiv; + unsigned int odiv; +}; + +struct imx_fracn_gppll_clk { + const struct imx_fracn_gppll_rate_table *rate_table; + int rate_count; + int flags; +}; + +struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base, + const struct imx_fracn_gppll_clk *pll_clk); + +extern struct imx_fracn_gppll_clk imx_fracn_gppll; + +#define imx_clk_cpu(name, parent_name, div, mux, pll, step) \ + to_clk(imx_clk_hw_cpu(name, parent_name, div, mux, pll, step)) + +#define clk_register_gate2(dev, name, parent_name, flags, reg, bit_idx, \ + cgr_val, cgr_mask, clk_gate_flags, lock, share_count) \ + to_clk(clk_hw_register_gate2(dev, name, parent_name, flags, reg, bit_idx, \ + cgr_val, cgr_mask, clk_gate_flags, lock, share_count)) + +#define imx_clk_pllv3(type, name, parent_name, base, div_mask) \ + to_clk(imx_clk_hw_pllv3(type, name, parent_name, base, div_mask)) + +#define imx_clk_pfd(name, parent_name, reg, idx) \ + to_clk(imx_clk_hw_pfd(name, parent_name, reg, idx)) + +#define imx_clk_gate_exclusive(name, parent, reg, shift, exclusive_mask) \ + to_clk(imx_clk_hw_gate_exclusive(name, parent, reg, shift, exclusive_mask)) + +#define imx_clk_fixed(name, rate) \ + to_clk(imx_clk_hw_fixed(name, rate)) + +#define imx_clk_fixed_factor(name, parent, mult, div) \ + to_clk(imx_clk_hw_fixed_factor(name, parent, mult, div)) + +#define imx_clk_divider(name, parent, reg, shift, width) \ + to_clk(imx_clk_hw_divider(name, parent, reg, shift, width)) + +#define imx_clk_divider_flags(name, parent, reg, shift, width, flags) \ + to_clk(imx_clk_hw_divider_flags(name, parent, reg, shift, width, flags)) + +#define imx_clk_gate(name, parent, reg, shift) \ + to_clk(imx_clk_hw_gate(name, parent, reg, shift)) + +#define imx_clk_gate_dis(name, parent, reg, shift) \ + to_clk(imx_clk_hw_gate_dis(name, parent, reg, shift)) + +#define imx_clk_gate2(name, parent, reg, shift) \ + to_clk(imx_clk_hw_gate2(name, parent, reg, shift)) + +#define imx_clk_gate2_cgr(name, parent, reg, shift, cgr_val) \ + to_clk(__imx_clk_hw_gate2(name, parent, reg, shift, cgr_val, 0, NULL)) + +#define imx_clk_gate2_flags(name, parent, reg, shift, flags) \ + to_clk(imx_clk_hw_gate2_flags(name, parent, reg, shift, flags)) + +#define imx_clk_mux(name, reg, shift, width, parents, num_parents) \ + to_clk(imx_clk_hw_mux(name, reg, shift, width, parents, num_parents)) + +#define imx_clk_mux_flags(name, reg, shift, width, parents, num_parents, flags) \ + to_clk(imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags)) + +#define imx_clk_mux2_flags(name, reg, shift, width, parents, num_parents, flags) \ + to_clk(imx_clk_hw_mux2_flags(name, reg, shift, width, parents, num_parents, flags)) + +#define imx_clk_pllv1(type, name, parent, base) \ + to_clk(imx_clk_hw_pllv1(type, name, parent, base)) + +#define imx_clk_pllv2(name, parent, base) \ + to_clk(imx_clk_hw_pllv2(name, parent, base)) + +#define imx_clk_mux_flags(name, reg, shift, width, parents, num_parents, flags) \ + to_clk(imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags)) + +#define imx_clk_hw_gate(name, parent, reg, shift) \ + imx_clk_hw_gate_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate2(name, parent, reg, shift) \ + imx_clk_hw_gate2_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate_dis(name, parent, reg, shift) \ + imx_clk_hw_gate_dis_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate_dis_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate(name, parent, reg, shift, flags, CLK_GATE_SET_TO_DISABLE) + +#define imx_clk_hw_gate_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate(name, parent, reg, shift, flags, 0) + +#define imx_clk_hw_gate2_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate2(name, parent, reg, shift, 0x3, flags, NULL) + +#define imx_clk_hw_gate2_shared(name, parent, reg, shift, shared_count) \ + __imx_clk_hw_gate2(name, parent, reg, shift, 0x3, 0, shared_count) + +#define imx_clk_hw_gate2_shared2(name, parent, reg, shift, shared_count) \ + __imx_clk_hw_gate2(name, parent, reg, shift, 0x3, CLK_OPS_PARENT_ENABLE, shared_count) + +#define imx_clk_hw_gate3(name, parent, reg, shift) \ + imx_clk_hw_gate3_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate3_flags(name, parent, reg, shift, flags) \ + __imx_clk_hw_gate(name, parent, reg, shift, flags | CLK_OPS_PARENT_ENABLE, 0) + +#define imx_clk_hw_gate4(name, parent, reg, shift) \ + imx_clk_hw_gate4_flags(name, parent, reg, shift, 0) + +#define imx_clk_hw_gate4_flags(name, parent, reg, shift, flags) \ + imx_clk_hw_gate2_flags(name, parent, reg, shift, flags | CLK_OPS_PARENT_ENABLE) + +#define imx_clk_hw_mux2(name, reg, shift, width, parents, num_parents) \ + imx_clk_hw_mux2_flags(name, reg, shift, width, parents, num_parents, 0) + +#define imx_clk_hw_mux(name, reg, shift, width, parents, num_parents) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, 0, 0) + +#define imx_clk_hw_mux_flags(name, reg, shift, width, parents, num_parents, flags) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, flags, 0) + +#define imx_clk_hw_mux_ldb(name, reg, shift, width, parents, num_parents) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, CLK_SET_RATE_PARENT, CLK_MUX_READ_ONLY) + +#define imx_clk_hw_mux2_flags(name, reg, shift, width, parents, num_parents, flags) \ + __imx_clk_hw_mux(name, reg, shift, width, parents, num_parents, flags | CLK_OPS_PARENT_ENABLE, 0) + +#define imx_clk_hw_divider(name, parent, reg, shift, width) \ + __imx_clk_hw_divider(name, parent, reg, shift, width, CLK_SET_RATE_PARENT) + +#define imx_clk_hw_divider2(name, parent, reg, shift, width) \ + __imx_clk_hw_divider(name, parent, reg, shift, width, \ + CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE) + +#define imx_clk_hw_divider_flags(name, parent, reg, shift, width, flags) \ + __imx_clk_hw_divider(name, parent, reg, shift, width, flags) + +#define imx_clk_hw_pll14xx(name, parent_name, base, pll_clk) \ + imx_dev_clk_hw_pll14xx(NULL, name, parent_name, base, pll_clk) + +struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name, + const char *parent_name, void __iomem *base, + const struct imx_pll14xx_clk *pll_clk); + +struct clk_hw *imx_clk_hw_pllv1(enum imx_pllv1_type type, const char *name, + const char *parent, void __iomem *base); + +struct clk_hw *imx_clk_hw_pllv2(const char *name, const char *parent, + void __iomem *base); + +struct clk_hw *imx_clk_hw_frac_pll(const char *name, const char *parent_name, + void __iomem *base); + +struct clk_hw *imx_clk_hw_sscg_pll(const char *name, + const char * const *parent_names, + u8 num_parents, + u8 parent, u8 bypass1, u8 bypass2, + void __iomem *base, + unsigned long flags); + +enum imx_pllv3_type { + IMX_PLLV3_GENERIC, + IMX_PLLV3_SYS, + IMX_PLLV3_USB, + IMX_PLLV3_USB_VF610, + IMX_PLLV3_AV, + IMX_PLLV3_ENET, + IMX_PLLV3_ENET_IMX7, + IMX_PLLV3_SYS_VF610, + IMX_PLLV3_DDR_IMX7, + IMX_PLLV3_AV_IMX7, +}; + +struct clk_hw *imx_clk_hw_pllv3(enum imx_pllv3_type type, const char *name, + const char *parent_name, void __iomem *base, u32 div_mask); + +#define PLL_1416X_RATE(_rate, _m, _p, _s) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + } + +#define PLL_1443X_RATE(_rate, _m, _p, _s, _k) \ + { \ + .rate = (_rate), \ + .mdiv = (_m), \ + .pdiv = (_p), \ + .sdiv = (_s), \ + .kdiv = (_k), \ + } + +struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name, + const char *parent_name, void __iomem *base); + +struct clk_hw *clk_hw_register_gate2(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, + void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 cgr_mask, + u8 clk_gate_flags, spinlock_t *lock, + unsigned int *share_count); + +struct clk * imx_obtain_fixed_clock( + const char *name, unsigned long rate); + +struct clk_hw *imx_obtain_fixed_clock_hw( + const char *name, unsigned long rate); + +struct clk_hw *imx_obtain_fixed_clk_hw(struct device_node *np, + const char *name); + +struct clk_hw *imx_clk_hw_gate_exclusive(const char *name, const char *parent, + void __iomem *reg, u8 shift, u32 exclusive_mask); + +struct clk_hw *imx_clk_hw_pfd(const char *name, const char *parent_name, + void __iomem *reg, u8 idx); + +struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name, + const char *parent_name, void __iomem *reg, u8 idx); + +struct clk_hw *imx_clk_hw_busy_divider(const char *name, const char *parent_name, + void __iomem *reg, u8 shift, u8 width, + void __iomem *busy_reg, u8 busy_shift); + +struct clk_hw *imx_clk_hw_busy_mux(const char *name, void __iomem *reg, u8 shift, + u8 width, void __iomem *busy_reg, u8 busy_shift, + const char * const *parent_names, int num_parents); + +struct clk_hw *imx7ulp_clk_hw_composite(const char *name, + const char * const *parent_names, + int num_parents, bool mux_present, + bool rate_present, bool gate_present, + void __iomem *reg); + +struct clk_hw *imx8ulp_clk_hw_composite(const char *name, + const char * const *parent_names, + int num_parents, bool mux_present, + bool rate_present, bool gate_present, + void __iomem *reg, bool has_swrst); + +struct clk_hw *imx_clk_hw_fixup_divider(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 width, + void (*fixup)(u32 *val)); + +struct clk_hw *imx_clk_hw_fixup_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char * const *parents, + int num_parents, void (*fixup)(u32 *val)); + +static inline struct clk *to_clk(struct clk_hw *hw) +{ + if (IS_ERR_OR_NULL(hw)) + return ERR_CAST(hw); + return hw->clk; +} + +static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate) +{ + return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate); +} + +static inline struct clk_hw *imx_clk_hw_fixed_factor(const char *name, + const char *parent, unsigned int mult, unsigned int div) +{ + return clk_hw_register_fixed_factor(NULL, name, parent, + CLK_SET_RATE_PARENT, mult, div); +} + +static inline struct clk_hw *__imx_clk_hw_divider(const char *name, + const char *parent, + void __iomem *reg, u8 shift, + u8 width, unsigned long flags) +{ + return clk_hw_register_divider(NULL, name, parent, flags, + reg, shift, width, 0, &imx_ccm_lock); +} + +static inline struct clk_hw *__imx_clk_hw_gate(const char *name, const char *parent, + void __iomem *reg, u8 shift, + unsigned long flags, + unsigned long clk_gate_flags) +{ + return clk_hw_register_gate(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, + shift, clk_gate_flags, &imx_ccm_lock); +} + +static inline struct clk_hw *__imx_clk_hw_gate2(const char *name, const char *parent, + void __iomem *reg, u8 shift, u8 cgr_val, + unsigned long flags, + unsigned int *share_count) +{ + return clk_hw_register_gate2(NULL, name, parent, flags | CLK_SET_RATE_PARENT, reg, + shift, cgr_val, 0x3, 0, &imx_ccm_lock, share_count); +} + +static inline struct clk_hw *__imx_clk_hw_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char * const *parents, + int num_parents, unsigned long flags, unsigned long clk_mux_flags) +{ + return clk_hw_register_mux(NULL, name, parents, num_parents, + flags | CLK_SET_RATE_NO_REPARENT, reg, shift, + width, clk_mux_flags, &imx_ccm_lock); +} + +struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name, + struct clk *div, struct clk *mux, struct clk *pll, + struct clk *step); + +#define IMX_COMPOSITE_CORE BIT(0) +#define IMX_COMPOSITE_BUS BIT(1) +#define IMX_COMPOSITE_FW_MANAGED BIT(2) + +#define IMX_COMPOSITE_CLK_FLAGS_DEFAULT \ + (CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) +#define IMX_COMPOSITE_CLK_FLAGS_CRITICAL \ + (IMX_COMPOSITE_CLK_FLAGS_DEFAULT | CLK_IS_CRITICAL) +#define IMX_COMPOSITE_CLK_FLAGS_GET_RATE_NO_CACHE \ + (IMX_COMPOSITE_CLK_FLAGS_DEFAULT | CLK_GET_RATE_NOCACHE) +#define IMX_COMPOSITE_CLK_FLAGS_CRITICAL_GET_RATE_NO_CACHE \ + (IMX_COMPOSITE_CLK_FLAGS_GET_RATE_NO_CACHE | CLK_IS_CRITICAL) + +struct clk_hw *__imx8m_clk_hw_composite(const char *name, + const char * const *parent_names, + int num_parents, + void __iomem *reg, + u32 composite_flags, + unsigned long flags); + +#define _imx8m_clk_hw_composite(name, parent_names, reg, composite_flags, flags) \ + __imx8m_clk_hw_composite(name, parent_names, \ + ARRAY_SIZE(parent_names), reg, composite_flags, flags) + +#define imx8m_clk_hw_composite(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + 0, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) + +#define imx8m_clk_hw_composite_critical(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + 0, IMX_COMPOSITE_CLK_FLAGS_CRITICAL) + +#define imx8m_clk_hw_composite_bus(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_BUS, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) + +#define imx8m_clk_hw_composite_bus_critical(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_BUS, IMX_COMPOSITE_CLK_FLAGS_CRITICAL) + +#define imx8m_clk_hw_composite_core(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_CORE, IMX_COMPOSITE_CLK_FLAGS_DEFAULT) + +#define imx8m_clk_hw_fw_managed_composite(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_FW_MANAGED, \ + IMX_COMPOSITE_CLK_FLAGS_GET_RATE_NO_CACHE) + +#define imx8m_clk_hw_fw_managed_composite_critical(name, parent_names, reg) \ + _imx8m_clk_hw_composite(name, parent_names, reg, \ + IMX_COMPOSITE_FW_MANAGED, \ + IMX_COMPOSITE_CLK_FLAGS_CRITICAL_GET_RATE_NO_CACHE) + +struct clk_hw *imx93_clk_composite_flags(const char *name, + const char * const *parent_names, + int num_parents, + void __iomem *reg, + u32 domain_id, + unsigned long flags); +#define imx93_clk_composite(name, parent_names, num_parents, reg, domain_id) \ + imx93_clk_composite_flags(name, parent_names, num_parents, reg, domain_id \ + CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) + +struct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val, + u32 mask, u32 domain_id, unsigned int *share_count); + +struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock); +#endif |