help
Enable this to support the clocks
+config SPL_CLK_CCF
+ bool "SPL Common Clock Framework [CCF] support "
+ depends on SPL_CLK_IMX6Q
+ help
+ Enable this option if you want to (re-)use the Linux kernel's Common
+ Clock Framework [CCF] code in U-Boot's SPL.
+
+config CLK_CCF
+ bool "Common Clock Framework [CCF] support "
+ depends on CLK_IMX6Q
+ help
+ Enable this option if you want to (re-)use the Linux kernel's Common
+ Clock Framework [CCF] code in U-Boot's clock driver.
+
config CLK_STM32F
bool "Enable clock driver support for STM32F family"
depends on CLK && (STM32F7 || STM32F4)
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
obj-y += imx/
obj-y += tegra/
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
+
+static unsigned int _get_table_div(const struct clk_div_table *table,
+ unsigned int val)
+{
+ const struct clk_div_table *clkt;
+
+ for (clkt = table; clkt->div; clkt++)
+ if (clkt->val == val)
+ return clkt->div;
+ return 0;
+}
+
+static unsigned int _get_div(const struct clk_div_table *table,
+ unsigned int val, unsigned long flags, u8 width)
+{
+ if (flags & CLK_DIVIDER_ONE_BASED)
+ return val;
+ if (flags & CLK_DIVIDER_POWER_OF_TWO)
+ return 1 << val;
+ if (flags & CLK_DIVIDER_MAX_AT_ZERO)
+ return val ? val : clk_div_mask(width) + 1;
+ if (table)
+ return _get_table_div(table, val);
+ return val + 1;
+}
+
+unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
+ unsigned int val,
+ const struct clk_div_table *table,
+ unsigned long flags, unsigned long width)
+{
+ unsigned int div;
+
+ div = _get_div(table, val, flags, width);
+ if (!div) {
+ WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
+ "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+ clk_hw_get_name(hw));
+ return parent_rate;
+ }
+
+ return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+}
+
+static ulong clk_divider_recalc_rate(struct clk *clk)
+{
+ struct clk_divider *divider =
+ to_clk_divider(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+ unsigned int val;
+
+ val = readl(divider->reg) >> divider->shift;
+ val &= clk_div_mask(divider->width);
+
+ return divider_recalc_rate(clk, parent_rate, val, divider->table,
+ divider->flags, divider->width);
+}
+
+const struct clk_ops clk_divider_ops = {
+ .get_rate = clk_divider_recalc_rate,
+};
+
+static struct clk *_register_divider(struct device *dev, 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)
+{
+ struct clk_divider *div;
+ struct clk *clk;
+ int ret;
+
+ if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
+ if (width + shift > 16) {
+ pr_warn("divider value exceeds LOWORD field\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ /* allocate the divider */
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ /* struct clk_divider assignments */
+ div->reg = reg;
+ div->shift = shift;
+ div->width = width;
+ div->flags = clk_divider_flags;
+ div->table = table;
+
+ /* register the clock */
+ clk = &div->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
+ if (ret) {
+ kfree(div);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags)
+{
+ struct clk *clk;
+
+ clk = _register_divider(dev, name, parent_name, flags, reg, shift,
+ width, clk_divider_flags, NULL);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+ return clk;
+}
+
+U_BOOT_DRIVER(ccf_clk_divider) = {
+ .name = UBOOT_DM_CLK_CCF_DIVIDER,
+ .id = UCLASS_CLK,
+ .ops = &clk_divider_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ */
+#include <common.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_FIXED_FACTOR "ccf_clk_fixed_factor"
+
+static ulong clk_factor_recalc_rate(struct clk *clk)
+{
+ struct clk_fixed_factor *fix =
+ to_clk_fixed_factor(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+ unsigned long long int rate;
+
+ rate = (unsigned long long int)parent_rate * fix->mult;
+ do_div(rate, fix->div);
+ return (ulong)rate;
+}
+
+const struct clk_ops ccf_clk_fixed_factor_ops = {
+ .get_rate = clk_factor_recalc_rate,
+};
+
+struct clk *clk_hw_register_fixed_factor(struct device *dev,
+ const char *name, const char *parent_name, unsigned long flags,
+ unsigned int mult, unsigned int div)
+{
+ struct clk_fixed_factor *fix;
+ struct clk *clk;
+ int ret;
+
+ fix = kzalloc(sizeof(*fix), GFP_KERNEL);
+ if (!fix)
+ return ERR_PTR(-ENOMEM);
+
+ /* struct clk_fixed_factor assignments */
+ fix->mult = mult;
+ fix->div = div;
+ clk = &fix->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, name,
+ parent_name);
+ if (ret) {
+ kfree(fix);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ unsigned int mult, unsigned int div)
+{
+ struct clk *clk;
+
+ clk = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
+ div);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+ return clk;
+}
+
+U_BOOT_DRIVER(imx_clk_fixed_factor) = {
+ .name = UBOOT_DM_CLK_IMX_FIXED_FACTOR,
+ .id = UCLASS_CLK,
+ .ops = &ccf_clk_fixed_factor_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * Simple multiplexer clock implementation
+ */
+
+/*
+ * U-Boot CCF porting node:
+ *
+ * The Linux kernel - as of tag: 5.0-rc3 is using also the imx_clk_fixup_mux()
+ * version of CCF mux. It is used on e.g. imx6q to provide fixes (like
+ * imx_cscmr1_fixup) for broken HW.
+ *
+ * At least for IMX6Q (but NOT IMX6QP) it is important when we set the parent
+ * clock.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_CCF_MUX "ccf_clk_mux"
+
+int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
+ unsigned int val)
+{
+ struct clk_mux *mux = to_clk_mux(clk);
+ int num_parents = mux->num_parents;
+
+ if (table) {
+ int i;
+
+ for (i = 0; i < num_parents; i++)
+ if (table[i] == val)
+ return i;
+ return -EINVAL;
+ }
+
+ if (val && (flags & CLK_MUX_INDEX_BIT))
+ val = ffs(val) - 1;
+
+ if (val && (flags & CLK_MUX_INDEX_ONE))
+ val--;
+
+ if (val >= num_parents)
+ return -EINVAL;
+
+ return val;
+}
+
+static u8 clk_mux_get_parent(struct clk *clk)
+{
+ struct clk_mux *mux = to_clk_mux(clk);
+ u32 val;
+
+ val = readl(mux->reg) >> mux->shift;
+ val &= mux->mask;
+
+ return clk_mux_val_to_index(clk, mux->table, mux->flags, val);
+}
+
+const struct clk_ops clk_mux_ops = {
+ .get_rate = clk_generic_get_rate,
+};
+
+struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table)
+{
+ struct clk_mux *mux;
+ struct clk *clk;
+ u8 width = 0;
+ int ret;
+
+ if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
+ width = fls(mask) - ffs(mask) + 1;
+ if (width + shift > 16) {
+ pr_err("mux value exceeds LOWORD field\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
+ /* allocate the mux */
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ /* U-boot specific assignments */
+ mux->parent_names = parent_names;
+ mux->num_parents = num_parents;
+
+ /* struct clk_mux assignments */
+ mux->reg = reg;
+ mux->shift = shift;
+ mux->mask = mask;
+ mux->flags = clk_mux_flags;
+ mux->table = table;
+
+ clk = &mux->clk;
+
+ /*
+ * Read the current mux setup - so we assign correct parent.
+ *
+ * Changing parent would require changing internals of udevice struct
+ * for the corresponding clock (to do that define .set_parent() method.
+ */
+ ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, name,
+ parent_names[clk_mux_get_parent(clk)]);
+ if (ret) {
+ kfree(mux);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+struct clk *clk_register_mux_table(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u32 mask,
+ u8 clk_mux_flags, u32 *table)
+{
+ struct clk *clk;
+
+ clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
+ flags, reg, shift, mask, clk_mux_flags,
+ table);
+ if (IS_ERR(clk))
+ return ERR_CAST(clk);
+ return clk;
+}
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags)
+{
+ u32 mask = BIT(width) - 1;
+
+ return clk_register_mux_table(dev, name, parent_names, num_parents,
+ flags, reg, shift, mask, clk_mux_flags,
+ NULL);
+}
+
+U_BOOT_DRIVER(ccf_clk_mux) = {
+ .name = UBOOT_DM_CLK_CCF_MUX,
+ .id = UCLASS_CLK,
+ .ops = &clk_mux_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <clk.h>
+
+int clk_register(struct clk *clk, const char *drv_name,
+ const char *name, const char *parent_name)
+{
+ struct udevice *parent;
+ struct driver *drv;
+ int ret;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, parent_name, &parent);
+ if (ret)
+ printf("%s: UCLASS parent: 0x%p\n", __func__, parent);
+
+ debug("%s: name: %s parent: %s [0x%p]\n", __func__, name, parent->name,
+ parent);
+
+ drv = lists_driver_lookup_name(drv_name);
+ if (!drv) {
+ printf("%s: %s is not a valid driver name\n",
+ __func__, drv_name);
+ return -ENOENT;
+ }
+
+ ret = device_bind(parent, drv, name, NULL, -1, &clk->dev);
+ if (ret) {
+ printf("%s: CLK: %s driver bind error [%d]!\n", __func__, name,
+ ret);
+ return ret;
+ }
+
+ /* Store back pointer to clk from udevice */
+ clk->dev->uclass_priv = clk;
+
+ return 0;
+}
+
+ulong clk_generic_get_rate(struct clk *clk)
+{
+ return clk_get_parent_rate(clk);
+}
+
+const char *clk_hw_get_name(const struct clk *hw)
+{
+ return hw->dev->name;
+}
+config SPL_CLK_IMX6Q
+ bool "SPL clock support for i.MX6Q"
+ depends on ARCH_MX6 && SPL
+ select SPL_CLK
+ select SPL_CLK_CCF
+ help
+ This enables SPL DM/DTS support for clock driver in i.MX6Q platforms.
+
+config CLK_IMX6Q
+ bool "Clock support for i.MX6Q"
+ depends on ARCH_MX6
+ select CLK
+ select CLK_CCF
+ help
+ This enables DM/DTS support for clock driver in i.MX6Q platforms.
+
config CLK_IMX8
bool "Clock support for i.MX8"
depends on ARCH_IMX8
#
# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-gate2.o clk-pllv3.o clk-pfd.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_IMX6Q) += clk-imx6q.o
obj-$(CONFIG_CLK_IMX8) += clk-imx8.o
ifdef CONFIG_CLK_IMX8
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Gated clock implementation
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2"
+
+struct clk_gate2 {
+ struct clk clk;
+ void __iomem *reg;
+ u8 bit_idx;
+ u8 cgr_val;
+ u8 flags;
+};
+
+#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk)
+
+static int clk_gate2_enable(struct clk *clk)
+{
+ struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ u32 reg;
+
+ reg = readl(gate->reg);
+ reg &= ~(3 << gate->bit_idx);
+ reg |= gate->cgr_val << gate->bit_idx;
+ writel(reg, gate->reg);
+
+ return 0;
+}
+
+static int clk_gate2_disable(struct clk *clk)
+{
+ struct clk_gate2 *gate = to_clk_gate2(dev_get_clk_ptr(clk->dev));
+ u32 reg;
+
+ reg = readl(gate->reg);
+ reg &= ~(3 << gate->bit_idx);
+ writel(reg, gate->reg);
+
+ return 0;
+}
+
+static const struct clk_ops clk_gate2_ops = {
+ .enable = clk_gate2_enable,
+ .disable = clk_gate2_disable,
+ .get_rate = clk_generic_get_rate,
+};
+
+struct clk *clk_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 clk_gate2_flags)
+{
+ struct clk_gate2 *gate;
+ struct clk *clk;
+ int ret;
+
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->reg = reg;
+ gate->bit_idx = bit_idx;
+ gate->cgr_val = cgr_val;
+ gate->flags = clk_gate2_flags;
+
+ clk = &gate->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, parent_name);
+ if (ret) {
+ kfree(gate);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_gate2) = {
+ .name = UBOOT_DM_CLK_IMX_GATE2,
+ .id = UCLASS_CLK,
+ .ops = &clk_gate2_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <dt-bindings/clock/imx6qdl-clock.h>
+
+#include "clk.h"
+
+static int imx6q_check_id(ulong id)
+{
+ if (id < IMX6QDL_CLK_DUMMY || id >= IMX6QDL_CLK_END) {
+ printf("%s: Invalid clk ID #%lu\n", __func__, id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ulong imx6q_clk_get_rate(struct clk *clk)
+{
+ struct clk *c;
+ int ret;
+
+ debug("%s(#%lu)\n", __func__, clk->id);
+
+ ret = imx6q_check_id(clk->id);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ return clk_get_rate(c);
+}
+
+static ulong imx6q_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
+
+ return rate;
+}
+
+static int __imx6q_clk_enable(struct clk *clk, bool enable)
+{
+ struct clk *c;
+ int ret = 0;
+
+ debug("%s(#%lu) en: %d\n", __func__, clk->id, enable);
+
+ ret = imx6q_check_id(clk->id);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ if (enable)
+ ret = clk_enable(c);
+ else
+ ret = clk_disable(c);
+
+ return ret;
+}
+
+static int imx6q_clk_disable(struct clk *clk)
+{
+ return __imx6q_clk_enable(clk, 0);
+}
+
+static int imx6q_clk_enable(struct clk *clk)
+{
+ return __imx6q_clk_enable(clk, 1);
+}
+
+static struct clk_ops imx6q_clk_ops = {
+ .set_rate = imx6q_clk_set_rate,
+ .get_rate = imx6q_clk_get_rate,
+ .enable = imx6q_clk_enable,
+ .disable = imx6q_clk_disable,
+};
+
+static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
+
+static int imx6q_clk_probe(struct udevice *dev)
+{
+ void *base;
+
+ /* Anatop clocks */
+ base = (void *)ANATOP_BASE_ADDR;
+
+ clk_dm(IMX6QDL_CLK_PLL2,
+ imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc",
+ base + 0x30, 0x1));
+ clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
+ imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
+ base + 0x10, 0x3));
+ clk_dm(IMX6QDL_CLK_PLL3_60M,
+ imx_clk_fixed_factor("pll3_60m", "pll3_usb_otg", 1, 8));
+ clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
+ imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
+ clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M,
+ imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2));
+
+ /* CCM clocks */
+ base = dev_read_addr_ptr(dev);
+ if (base == (void *)FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ clk_dm(IMX6QDL_CLK_USDHC1_SEL,
+ imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+ clk_dm(IMX6QDL_CLK_USDHC2_SEL,
+ imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+ clk_dm(IMX6QDL_CLK_USDHC3_SEL,
+ imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+ clk_dm(IMX6QDL_CLK_USDHC4_SEL,
+ imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1,
+ usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+
+ clk_dm(IMX6QDL_CLK_USDHC1_PODF,
+ imx_clk_divider("usdhc1_podf", "usdhc1_sel",
+ base + 0x24, 11, 3));
+ clk_dm(IMX6QDL_CLK_USDHC2_PODF,
+ imx_clk_divider("usdhc2_podf", "usdhc2_sel",
+ base + 0x24, 16, 3));
+ clk_dm(IMX6QDL_CLK_USDHC3_PODF,
+ imx_clk_divider("usdhc3_podf", "usdhc3_sel",
+ base + 0x24, 19, 3));
+ clk_dm(IMX6QDL_CLK_USDHC4_PODF,
+ imx_clk_divider("usdhc4_podf", "usdhc4_sel",
+ base + 0x24, 22, 3));
+
+ clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
+ imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6));
+
+ clk_dm(IMX6QDL_CLK_ECSPI1,
+ imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
+ clk_dm(IMX6QDL_CLK_ECSPI2,
+ imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2));
+ clk_dm(IMX6QDL_CLK_ECSPI3,
+ imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4));
+ clk_dm(IMX6QDL_CLK_ECSPI4,
+ imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6));
+ clk_dm(IMX6QDL_CLK_USDHC1,
+ imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
+ clk_dm(IMX6QDL_CLK_USDHC2,
+ imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
+ clk_dm(IMX6QDL_CLK_USDHC3,
+ imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6));
+ clk_dm(IMX6QDL_CLK_USDHC4,
+ imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8));
+
+ return 0;
+}
+
+static const struct udevice_id imx6q_clk_ids[] = {
+ { .compatible = "fsl,imx6q-ccm" },
+ { },
+};
+
+U_BOOT_DRIVER(imx6q_clk) = {
+ .name = "clk_imx6q",
+ .id = UCLASS_CLK,
+ .of_match = imx6q_clk_ids,
+ .ops = &imx6q_clk_ops,
+ .probe = imx6q_clk_probe,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_PFD "imx_clk_pfd"
+
+struct clk_pfd {
+ struct clk clk;
+ void __iomem *reg;
+ u8 idx;
+};
+
+#define to_clk_pfd(_clk) container_of(_clk, struct clk_pfd, clk)
+
+#define SET 0x4
+#define CLR 0x8
+#define OTG 0xc
+
+static unsigned long clk_pfd_recalc_rate(struct clk *clk)
+{
+ struct clk_pfd *pfd =
+ to_clk_pfd(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+ u64 tmp = parent_rate;
+ u8 frac = (readl(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
+
+ tmp *= 18;
+ do_div(tmp, frac);
+
+ return tmp;
+}
+
+static const struct clk_ops clk_pfd_ops = {
+ .get_rate = clk_pfd_recalc_rate,
+};
+
+struct clk *imx_clk_pfd(const char *name, const char *parent_name,
+ void __iomem *reg, u8 idx)
+{
+ struct clk_pfd *pfd;
+ struct clk *clk;
+ int ret;
+
+ pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
+ if (!pfd)
+ return ERR_PTR(-ENOMEM);
+
+ pfd->reg = reg;
+ pfd->idx = idx;
+
+ /* register the clock */
+ clk = &pfd->clk;
+
+ ret = clk_register(clk, UBOOT_DM_CLK_IMX_PFD, name, parent_name);
+ if (ret) {
+ kfree(pfd);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_pfd) = {
+ .name = UBOOT_DM_CLK_IMX_PFD,
+ .id = UCLASS_CLK,
+ .ops = &clk_pfd_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_PLLV3 "imx_clk_pllv3"
+
+struct clk_pllv3 {
+ struct clk clk;
+ void __iomem *base;
+ u32 div_mask;
+ u32 div_shift;
+};
+
+#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
+
+static ulong clk_pllv3_get_rate(struct clk *clk)
+{
+ struct clk_pllv3 *pll = to_clk_pllv3(dev_get_clk_ptr(clk->dev));
+ unsigned long parent_rate = clk_get_parent_rate(clk);
+
+ u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
+
+ return (div == 1) ? parent_rate * 22 : parent_rate * 20;
+}
+
+static const struct clk_ops clk_pllv3_generic_ops = {
+ .get_rate = clk_pllv3_get_rate,
+};
+
+struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
+ const char *parent_name, void __iomem *base,
+ u32 div_mask)
+{
+ struct clk_pllv3 *pll;
+ struct clk *clk;
+ char *drv_name;
+ int ret;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ switch (type) {
+ case IMX_PLLV3_GENERIC:
+ case IMX_PLLV3_USB:
+ drv_name = UBOOT_DM_CLK_IMX_PLLV3;
+ break;
+ default:
+ kfree(pll);
+ return ERR_PTR(-ENOTSUPP);
+ }
+
+ pll->base = base;
+ pll->div_mask = div_mask;
+ clk = &pll->clk;
+
+ ret = clk_register(clk, drv_name, name, parent_name);
+ if (ret) {
+ kfree(pll);
+ return ERR_PTR(ret);
+ }
+
+ return clk;
+}
+
+U_BOOT_DRIVER(clk_pllv3_generic) = {
+ .name = UBOOT_DM_CLK_IMX_PLLV3,
+ .id = UCLASS_CLK,
+ .ops = &clk_pllv3_generic_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+#ifndef __MACH_IMX_CLK_H
+#define __MACH_IMX_CLK_H
+
+#include <linux/clk-provider.h>
+
+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,
+};
+
+struct clk *clk_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 clk_gate_flags);
+
+struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
+ const char *parent_name, void __iomem *base,
+ u32 div_mask);
+
+static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
+ void __iomem *reg, u8 shift)
+{
+ return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+ shift, 0x3, 0);
+}
+
+static inline struct clk *imx_clk_fixed_factor(const char *name,
+ const char *parent, unsigned int mult, unsigned int div)
+{
+ return clk_register_fixed_factor(NULL, name, parent,
+ CLK_SET_RATE_PARENT, mult, div);
+}
+
+static inline struct clk *imx_clk_divider(const char *name, const char *parent,
+ void __iomem *reg, u8 shift, u8 width)
+{
+ return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+ reg, shift, width, 0);
+}
+
+struct clk *imx_clk_pfd(const char *name, const char *parent_name,
+ void __iomem *reg, u8 idx);
+
+struct clk *imx_clk_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 *imx_clk_mux(const char *name, void __iomem *reg,
+ u8 shift, u8 width, const char * const *parents,
+ int num_parents)
+{
+ return clk_register_mux(NULL, name, parents, num_parents,
+ CLK_SET_RATE_NO_REPARENT, reg, shift,
+ width, 0);
+}
+
+#endif /* __MACH_IMX_CLK_H */
#ifndef __LINUX_CLK_PROVIDER_H
#define __LINUX_CLK_PROVIDER_H
+static inline void clk_dm(ulong id, struct clk *clk)
+{
+ if (!IS_ERR(clk))
+ clk->id = id;
+}
+
+/*
+ * flags used across common struct clk. these flags should only affect the
+ * top-level framework. custom flags for dealing with hardware specifics
+ * belong in struct clk_foo
+ *
+ * Please update clk_flags[] in drivers/clk/clk.c when making changes here!
+ */
+#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */
+#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */
+#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */
+#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */
+ /* unused */
+#define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */
+#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */
+#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
+#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
+#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */
+#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */
+#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */
+/* parents need enable during gate/ungate, set rate and re-parent */
+#define CLK_OPS_PARENT_ENABLE BIT(12)
+/* duty cycle call may be forwarded to the parent clock */
+#define CLK_DUTY_CYCLE_PARENT BIT(13)
+
+#define CLK_MUX_INDEX_ONE BIT(0)
+#define CLK_MUX_INDEX_BIT BIT(1)
+#define CLK_MUX_HIWORD_MASK BIT(2)
+#define CLK_MUX_READ_ONLY BIT(3) /* mux can't be changed */
+#define CLK_MUX_ROUND_CLOSEST BIT(4)
+
+struct clk_mux {
+ struct clk clk;
+ void __iomem *reg;
+ u32 *table;
+ u32 mask;
+ u8 shift;
+ u8 flags;
+
+ /*
+ * Fields from struct clk_init_data - this struct has been
+ * omitted to avoid too deep level of CCF for bootloader
+ */
+ const char * const *parent_names;
+ u8 num_parents;
+};
+
+#define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk)
+
+struct clk_div_table {
+ unsigned int val;
+ unsigned int div;
+};
+
+struct clk_divider {
+ struct clk clk;
+ void __iomem *reg;
+ u8 shift;
+ u8 width;
+ u8 flags;
+ const struct clk_div_table *table;
+};
+
+#define clk_div_mask(width) ((1 << (width)) - 1)
+#define to_clk_divider(_clk) container_of(_clk, struct clk_divider, clk)
+
+#define CLK_DIVIDER_ONE_BASED BIT(0)
+#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
+#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
+#define CLK_DIVIDER_HIWORD_MASK BIT(3)
+#define CLK_DIVIDER_ROUND_CLOSEST BIT(4)
+#define CLK_DIVIDER_READ_ONLY BIT(5)
+#define CLK_DIVIDER_MAX_AT_ZERO BIT(6)
+
+struct clk_fixed_factor {
+ struct clk clk;
+ unsigned int mult;
+ unsigned int div;
+};
+
+#define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\
+ clk)
+
+int clk_register(struct clk *clk, const char *drv_name, const char *name,
+ const char *parent_name);
+
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ unsigned int mult, unsigned int div);
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+ const char *parent_name, unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_divider_flags);
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+ const char * const *parent_names, u8 num_parents,
+ unsigned long flags,
+ void __iomem *reg, u8 shift, u8 width,
+ u8 clk_mux_flags);
+
+const char *clk_hw_get_name(const struct clk *hw);
+ulong clk_generic_get_rate(struct clk *clk);
+
static inline struct clk *dev_get_clk_ptr(struct udevice *dev)
{
return (struct clk *)dev_get_uclass_priv(dev);