--- /dev/null
- [IMX95_CLK_DISPMIX_LVDS_CH0_GATE] = {
+ // 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_CH1_GATE] = {
++ [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_PIX_DI0_GATE] = {
++ },
++ [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_DI1_GATE] = {
++ },
++ [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,
- 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));
++ },
+ };
+
+ 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) {
- 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));
++ 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_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));
++ 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,
+ };
};
struct pcie_chip_info {
+ u32 flags;
+ const u32 ltssm_off;
+ const u32 ltssm_mask;
+ const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
+ const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
const char *gpr;
+ void (*init_phy)(struct pcie_dw_imx *priv);
+ int (*enable_ref_clk)(struct pcie_dw_imx *priv, bool enable);
+ int (*core_reset)(struct pcie_dw_imx *priv, bool assert);
+ int (*wait_pll_lock)(struct pcie_dw_imx *priv);
+ void (*post_config)(struct pcie_dw_imx *priv);
};
- IMX95_PCIE_SYS_AUX_PWR_DET, IMX95_PCIE_SYS_AUX_PWR_DET);
+ static void imx95_pcie_init_phy(struct pcie_dw_imx *priv)
+ {
+ /*
+ * Workaround for ERR051624: The Controller Without Vaux Cannot
+ * Exit L23 Ready Through Beacon or PERST# De-assertion
+ *
+ * When the auxiliary power is not available the controller
+ * cannot exit from L23 Ready with beacon or PERST# de-assertion
+ * when main power is not removed.
+ *
+ * Workaround: Set SS_RW_REG_1[SYS_AUX_PWR_DET] to 1.
+ */
+ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1,
- regmap_update_bits(priv->iomuxc_gpr,
- IMX95_PCIE_SS_RW_REG_0,
- IMX95_PCIE_PHY_CR_PARA_SEL,
- IMX95_PCIE_PHY_CR_PARA_SEL);
++ IMX95_PCIE_SYS_AUX_PWR_DET,
++ IMX95_PCIE_SYS_AUX_PWR_DET);
+
- regmap_update_bits(priv->iomuxc_gpr,
- IMX95_PCIE_PHY_GEN_CTRL,
- IMX95_PCIE_REF_USE_PAD,
- IMX95_PCIE_REF_USE_PAD);
- regmap_update_bits(priv->iomuxc_gpr,
- IMX95_PCIE_SS_RW_REG_0,
- IMX95_PCIE_REF_CLKEN, 0);
++ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
++ IMX95_PCIE_PHY_CR_PARA_SEL,
++ IMX95_PCIE_PHY_CR_PARA_SEL);
+
+ if (priv->enable_ext_refclk) {
+ /* External clock is used as reference clock */
- regmap_update_bits(priv->iomuxc_gpr,
- IMX95_PCIE_PHY_GEN_CTRL,
- IMX95_PCIE_REF_USE_PAD, 0);
++ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
++ IMX95_PCIE_REF_USE_PAD,
++ IMX95_PCIE_REF_USE_PAD);
++ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
++ IMX95_PCIE_REF_CLKEN, 0);
+ } else {
- regmap_update_bits(priv->iomuxc_gpr,
- IMX95_PCIE_SS_RW_REG_0,
- IMX95_PCIE_REF_CLKEN,
- IMX95_PCIE_REF_CLKEN);
++ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
++ IMX95_PCIE_REF_USE_PAD, 0);
+
- regmap_read(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
- &val);
++ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
++ IMX95_PCIE_REF_CLKEN, IMX95_PCIE_REF_CLKEN);
+ }
+
+ /* Force CLKREQ# low by override */
+ if (!priv->supports_clkreq)
+ regmap_update_bits(priv->iomuxc_gpr,
+ IMX95_PCIE_SS_RW_REG_1,
+ IMX95_PCIE_CLKREQ_OVERRIDE_EN |
+ IMX95_PCIE_CLKREQ_OVERRIDE_VAL,
+ IMX95_PCIE_CLKREQ_OVERRIDE_EN |
+ IMX95_PCIE_CLKREQ_OVERRIDE_VAL);
+ }
+
+ static int imx95_pcie_wait_for_phy_pll_lock(struct pcie_dw_imx *priv)
+ {
+ u32 val;
+
+ if (regmap_read_poll_timeout(priv->iomuxc_gpr,
+ IMX95_PCIE_PHY_MPLLA_CTRL, val,
+ val & IMX95_PCIE_PHY_MPLL_STATE,
+ PHY_PLL_LOCK_WAIT_USLEEP_MAX,
+ PHY_PLL_LOCK_WAIT_TIMEOUT)) {
+ printf("PCIe PLL lock timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+ }
+
+ static int imx95_pcie_core_reset(struct pcie_dw_imx *priv, bool assert)
+ {
+ u32 val;
+
+ if (assert) {
+ /*
+ * From i.MX95 PCIe PHY perspective, the COLD reset toggle
+ * should be complete after power-up by the following sequence.
+ * > 10us(at power-up)
+ * > 10ns(warm reset)
+ * |<------------>|
+ * ______________
+ * phy_reset ____/ \________________
+ * ____________
+ * ref_clk_en_______________________/
+ * Toggle COLD reset aligned with this sequence for i.MX95 PCIe.
+ */
+ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
+ IMX95_PCIE_COLD_RST, IMX95_PCIE_COLD_RST);
+ /*
+ * Make sure the write to IMX95_PCIE_RST_CTRL is flushed to the
+ * hardware by doing a read. Otherwise, there is no guarantee
+ * that the write has reached the hardware before udelay().
+ */
- IMX95_PCIE_COLD_RST, 0);
- regmap_read(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
- &val);
++ regmap_read(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL, &val);
+ udelay(15);
+ regmap_update_bits(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL,
++ IMX95_PCIE_COLD_RST, 0);
++ regmap_read(priv->iomuxc_gpr, IMX95_PCIE_RST_CTRL, &val);
+ udelay(10);
+ }
+
+ return 0;
+ }
+
+ static void imx95_pcie_post_config(struct pcie_dw_imx *priv)
+ {
+ u32 val;
+
+ /*
+ * Workaround for ERR051586: Compliance with 8GT/s Receiver
+ * Impedance ECN
+ *
+ * The default value of GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL] is
+ * 1 which makes receiver non-compliant with the ZRX-DC
+ * parameter for 2.5 GT/s when operating at 8 GT/s or higher. It
+ * causes unnecessary timeout in L1.
+ *
+ * Workaround: Program GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL] to 0.
+ */
+ dw_pcie_dbi_write_enable(&priv->dw, true);
+ val = readl(priv->dw.dbi_base + GEN3_RELATED_OFF);
+ val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
+ writel(val, priv->dw.dbi_base + GEN3_RELATED_OFF);
+ dw_pcie_dbi_write_enable(&priv->dw, false);
+ }
+
+ static int imx8mm_pcie_enable_ref_clk(struct pcie_dw_imx *priv, bool enable)
+ {
+ regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
+ IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE,
+ enable ? 0 : IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE);
+ regmap_update_bits(priv->iomuxc_gpr, IOMUXC_GPR14_OFFSET,
+ IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
+ enable ? IMX8M_GPR_PCIE_CLK_REQ_OVERRIDE_EN : 0);
+ return 0;
+ }
+
static const struct pcie_chip_info imx8mm_chip_info = {
+ .flags = IMX_PCIE_FLAG_HAS_APP_RESET | IMX_PCIE_FLAG_HAS_PHYDRV,
.gpr = "fsl,imx8mm-iomuxc-gpr",
+ .enable_ref_clk = imx8mm_pcie_enable_ref_clk,
};
static const struct pcie_chip_info imx8mp_chip_info = {