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...@nxp.com> --- drivers/clk/imx/Kconfig | 9 ++ drivers/clk/imx/Makefile | 1 + drivers/clk/imx/clk-imx95-blkctrl.c | 170 ++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) create mode 100644 drivers/clk/imx/clk-imx95-blkctrl.c diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig index 74d5fe73f944..644ab162af43 100644 --- a/drivers/clk/imx/Kconfig +++ b/drivers/clk/imx/Kconfig @@ -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. diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index b10221a195cb..f2fd6ff8ca07 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -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 index 000000000000..3bf6f9415e22 --- /dev/null +++ b/drivers/clk/imx/clk-imx95-blkctrl.c @@ -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, +}; -- 2.7.4