]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
clk: imx: Add imx95 blkctrl clock driver
authorYe Li <ye.li@nxp.com>
Thu, 11 Sep 2025 10:56:06 +0000 (18:56 +0800)
committerFabio Estevam <festevam@gmail.com>
Sat, 20 Sep 2025 20:46:15 +0000 (17:46 -0300)
Add iMX95 blkctrl clock driver which implements clocks for HSIOMIX
blkctrl and LVDS blkctrl.
Since multiple blkctrl device for different blkctrl may be enabled,
and each has dedicated clock id from 0. We must enable CLK_AUTO_ID
to avoid conflict on clock id.

Signed-off-by: Ye Li <ye.li@nxp.com>
drivers/clk/imx/Kconfig
drivers/clk/imx/Makefile
drivers/clk/imx/clk-imx95-blkctrl.c [new file with mode: 0644]

index 74d5fe73f94484ca67267368913ecf4e9d11f392..644ab162af4306b9c6383110ff993ff9a26bead8 100644 (file)
@@ -167,3 +167,12 @@ config CLK_IMXRT1170
        select CLK_CCF
        help
          This enables support clock driver for i.MXRT1170 platforms.
+
+config CLK_IMX95_BLKCTRL
+       bool "Enable i.MX95 blkctrl clock driver"
+       depends on IMX95 || IMX94
+       select CLK
+       select CLK_CCF
+       select CLK_AUTO_ID
+       help
+         Enable support for clocks in i.MX95 MIX blkctrl like HSIO and LVDS.
index b10221a195cb2a96db206bed59c408de8dce3761..f2fd6ff8ca0766a56d4077d47ceeee4bab606b6e 100644 (file)
@@ -25,3 +25,4 @@ obj-$(CONFIG_$(PHASE_)CLK_IMX93) += clk-imx93.o clk-fracn-gppll.o \
 obj-$(CONFIG_$(PHASE_)CLK_IMXRT1020) += clk-imxrt1020.o
 obj-$(CONFIG_$(PHASE_)CLK_IMXRT1050) += clk-imxrt1050.o
 obj-$(CONFIG_$(PHASE_)CLK_IMXRT1170) += clk-imxrt1170.o
+obj-$(CONFIG_CLK_IMX95_BLKCTRL) += clk-imx95-blkctrl.o
diff --git a/drivers/clk/imx/clk-imx95-blkctrl.c b/drivers/clk/imx/clk-imx95-blkctrl.c
new file mode 100644 (file)
index 0000000..3bf6f94
--- /dev/null
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023-2025 NXP
+ *
+ */
+
+#include <asm/io.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dt-bindings/clock/nxp,imx95-clock.h>
+#include <linux/clk-provider.h>
+
+#include "clk.h"
+
+enum {
+       CLK_GATE,
+       CLK_DIVIDER,
+       CLK_MUX,
+};
+
+struct imx95_blk_ctl_clk_dev_data {
+       const char *name;
+       const char * const *parent_names;
+       u32 num_parents;
+       u32 reg;
+       u32 bit_idx;
+       u32 clk_type;
+       u32 flags;
+       u32 flags2;
+       u32 type;
+};
+
+struct imx95_blk_ctl_dev_data {
+       const struct imx95_blk_ctl_clk_dev_data *clk_dev_data;
+       u32 num_clks;
+       u32 clk_reg_offset;
+};
+
+static const struct imx95_blk_ctl_clk_dev_data hsio_blk_ctl_clk_dev_data[] = {
+       [0] = {
+               .name = "hsio_blk_ctl_clk",
+               .parent_names = (const char *[]){ "hsiopll", },
+               .num_parents = 1,
+               .reg = 0,
+               .bit_idx = 6,
+               .type = CLK_GATE,
+               .flags = CLK_SET_RATE_PARENT,
+       }
+};
+
+static const struct imx95_blk_ctl_dev_data hsio_blk_ctl_dev_data = {
+       .num_clks = 1,
+       .clk_dev_data = hsio_blk_ctl_clk_dev_data,
+       .clk_reg_offset = 0,
+};
+
+static const struct imx95_blk_ctl_clk_dev_data imx95_lvds_clk_dev_data[] = {
+       [IMX95_CLK_DISPMIX_LVDS_PHY_DIV] = {
+               .name = "ldb_phy_div",
+               .parent_names = (const char *[]){ "ldbpll", },
+               .num_parents = 1,
+               .reg = 0,
+               .bit_idx = 0,
+               .type = CLK_DIVIDER,
+               .flags2 = CLK_DIVIDER_POWER_OF_TWO,
+       },
+
+    [IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = {
+               .name = "lvds_ch0_gate",
+               .parent_names = (const char *[]){ "ldb_phy_div", },
+               .num_parents = 1,
+               .reg = 0,
+               .bit_idx = 1,
+               .type = CLK_GATE,
+               .flags = CLK_SET_RATE_PARENT,
+               .flags2 = CLK_GATE_SET_TO_DISABLE,
+    },
+    [IMX95_CLK_DISPMIX_LVDS_CH1_GATE] = {
+               .name = "lvds_ch1_gate",
+               .parent_names = (const char *[]){ "ldb_phy_div", },
+               .num_parents = 1,
+               .reg = 0,
+               .bit_idx = 2,
+               .type = CLK_GATE,
+               .flags = CLK_SET_RATE_PARENT,
+               .flags2 = CLK_GATE_SET_TO_DISABLE,
+    },
+    [IMX95_CLK_DISPMIX_PIX_DI0_GATE] = {
+               .name = "lvds_di0_gate",
+               .parent_names = (const char *[]){ "ldb_pll_div7", },
+               .num_parents = 1,
+               .reg = 0,
+               .bit_idx = 3,
+               .type = CLK_GATE,
+               .flags = CLK_SET_RATE_PARENT,
+               .flags2 = CLK_GATE_SET_TO_DISABLE,
+    },
+    [IMX95_CLK_DISPMIX_PIX_DI1_GATE] = {
+               .name = "lvds_di1_gate",
+               .parent_names = (const char *[]){ "ldb_pll_div7", },
+               .num_parents = 1,
+               .reg = 0,
+               .bit_idx = 4,
+               .type = CLK_GATE,
+               .flags = CLK_SET_RATE_PARENT,
+               .flags2 = CLK_GATE_SET_TO_DISABLE,
+    },
+};
+
+static const struct imx95_blk_ctl_dev_data imx95_lvds_csr_dev_data = {
+       .num_clks = ARRAY_SIZE(imx95_lvds_clk_dev_data),
+       .clk_dev_data = imx95_lvds_clk_dev_data,
+       .clk_reg_offset = 0,
+};
+
+static int imx95_blkctrl_clk_probe(struct udevice *dev)
+{
+       int i;
+       void __iomem *addr;
+       struct imx95_blk_ctl_dev_data *dev_data = (void *)dev_get_driver_data(dev);
+       const struct imx95_blk_ctl_clk_dev_data *clk_dev_data;
+
+       addr = dev_read_addr_ptr(dev);
+       if (addr == (void *)FDT_ADDR_T_NONE) {
+               dev_err(dev, "No blkctrl register base address\n");
+               return -EINVAL;
+       }
+
+       if (!dev_data) {
+               dev_err(dev, "driver data is NULL\n");
+               return -EINVAL;
+       }
+
+       clk_dev_data = dev_data->clk_dev_data;
+       for (i = 0; i < dev_data->num_clks; i++) {
+               if (clk_dev_data[i].clk_type == CLK_GATE) {
+                       dev_clk_dm(dev, i, clk_register_gate(dev, clk_dev_data[i].name, clk_dev_data[i].parent_names[0],
+                                       clk_dev_data[i].flags, addr + dev_data->clk_reg_offset, clk_dev_data[i].bit_idx,
+                                       clk_dev_data[i].flags2, NULL));
+               } else if (clk_dev_data[i].clk_type == CLK_DIVIDER) {
+                       dev_clk_dm(dev, i,
+                               clk_register_divider(dev, clk_dev_data[i].name, clk_dev_data[i].parent_names[0],
+                                       clk_dev_data[i].flags, addr + dev_data->clk_reg_offset, clk_dev_data[i].bit_idx, 1,
+                                       clk_dev_data[i].flags2));
+               } else if (clk_dev_data[i].clk_type == CLK_MUX) {
+                       dev_clk_dm(dev, i,
+                               clk_register_mux(dev, clk_dev_data[i].name, clk_dev_data[i].parent_names,
+                                       clk_dev_data[i].num_parents, clk_dev_data[i].flags, addr + dev_data->clk_reg_offset,
+                                       clk_dev_data[i].bit_idx, 1, clk_dev_data[i].flags2));
+               }
+       }
+
+       return 0;
+}
+
+static const struct udevice_id imx95_blkctrl_clk_ids[] = {
+       { .compatible = "nxp,imx95-lvds-csr", .data = (ulong)&imx95_lvds_csr_dev_data, },
+       { .compatible = "nxp,imx95-hsio-blk-ctl", .data = (ulong)&hsio_blk_ctl_dev_data,  },
+       { },
+};
+
+U_BOOT_DRIVER(imx95_blkctrl_clk) = {
+       .name = "imx95_blkctrl_clk",
+       .id = UCLASS_CLK,
+       .of_match = imx95_blkctrl_clk_ids,
+       .ops = &ccf_clk_ops,
+       .probe = imx95_blkctrl_clk_probe,
+       .flags = DM_FLAG_PRE_RELOC,
+};