clk: imx6q: configure ldb clock selectors

A hardware bug prevents LDB clock selectors from being configured later
on non-plus i.MX6QD variants, so let's set the desired configuration in
the probe before we register them. We also have to make the necessary
clock functions available in XPL builds.

Signed-off-by: Brian Ruley <brian.ruley@gehealthcare.com>
This commit is contained in:
Brian Ruley
2026-06-26 23:02:46 -03:00
committed by Fabio Estevam
parent e3c70d44ff
commit db4aa45718
+119
View File
@@ -9,6 +9,7 @@
#include <log.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <dm/of_access.h>
#include <dt-bindings/clock/imx6qdl-clock.h>
#include "clk.h"
@@ -141,6 +142,121 @@ static const char *ipu2_di1_sels_2[] = {
};
static unsigned int share_count_mipi_core_cfg;
static void of_assigned_ldb_sels(struct udevice *dev, int *ldb_di0_sel,
int *ldb_di1_sel)
{
struct ofnode_phandle_args clk_args, parent_args;
ofnode node = dev_ofnode(dev);
int count, err;
count = dev_count_phandle_with_args(dev, "assigned-clocks",
"#clock-cells", 0);
if (count <= 0) {
if (count == 0)
debug("%s: no assigned_clocks found\n", dev->name);
else
pr_err("%s: failed to get phandle count (%d)\n",
dev->name, count);
return;
}
for (int i = 0; i < count; i++) {
err = dev_read_phandle_with_args(dev, "assigned-clocks",
"#clock-cells", 0, i,
&clk_args);
if (err == -ENOENT)
/* Skip empty handles */
continue;
else if (err < 0)
return;
if (!ofnode_equal(clk_args.node, node) ||
clk_args.args[0] >= IMX6QDL_CLK_END) {
pr_err("%s: clock %d not in ccm\n", dev->name, i);
return;
}
err = dev_read_phandle_with_args(dev, "assigned-clock-parents",
"#clock-cells", 0, i,
&parent_args);
if (err < 0)
return;
if (!ofnode_equal(parent_args.node, node) ||
parent_args.args[0] >= IMX6QDL_CLK_END) {
pr_err("%s: parent clock %d not in ccm\n", dev->name,
i);
return;
}
if (clk_args.args[0] == IMX6QDL_CLK_LDB_DI0_SEL)
*ldb_di0_sel = parent_args.args[0];
else if (clk_args.args[0] == IMX6QDL_CLK_LDB_DI1_SEL)
*ldb_di1_sel = parent_args.args[0];
}
}
static void imx6q_init_ldb_clks(struct udevice *dev)
{
int ldb_di_sel[] = { IMX6QDL_CLK_END, IMX6QDL_CLK_END };
enum ldb_di_clock ldb_di_clk[] = { MXC_MMDC_CH1_CLK, MXC_MMDC_CH1_CLK };
of_assigned_ldb_sels(dev, &ldb_di_sel[0], &ldb_di_sel[1]);
for (int i = 0; i < 2; i++) {
switch (ldb_di_sel[i]) {
case IMX6QDL_CLK_PLL5_VIDEO_DIV:
ldb_di_clk[i] = MXC_PLL5_CLK;
break;
case IMX6QDL_CLK_PLL2_PFD0_352M:
ldb_di_clk[i] = MXC_PLL2_PFD0_CLK;
break;
case IMX6QDL_CLK_PLL2_PFD2_396M: {
struct clk *clk, *parent;
int err = clk_get_by_id(IMX6QDL_CLK_PERIPH_PRE, &clk);
if (err) {
pr_err("%s: failed to get periph_pre clock "
"(%d)\n",
dev->name, err);
return;
}
err = clk_get_by_id(IMX6QDL_CLK_PLL2_PFD2_396M,
&parent);
if (err) {
pr_err("%s: failed to get pll2_pfd2_396m clock"
" (%d)\n",
dev->name, err);
return;
}
if (parent == clk) {
pr_err("%s: ldb_di%d_sel: couldn't disable "
"pll2_pfd2_396m clock\n",
dev->name, i);
return;
}
ldb_di_clk[i] = MXC_PLL2_PFD2_CLK;
break;
}
case IMX6QDL_CLK_MMDC_CH1_AXI:
case IMX6QDL_CLK_END:
/* use the default clock */
break;
case IMX6QDL_CLK_PLL3_USB_OTG:
ldb_di_clk[i] = MXC_PLL3_SW_CLK;
break;
default:
pr_err("%s: invalid LDB clock parent\n", dev->name);
return;
}
}
select_ldb_di_clock_source(ldb_di_clk[0], ldb_di_clk[1]);
}
#endif /* CONFIG_IS_ENABLED(VIDEO) */
static int imx6q_clk_probe(struct udevice *dev)
@@ -356,7 +472,10 @@ static int imx6q_clk_probe(struct udevice *dev)
/*
* Need to set these as read-only due to a hardware bug.
* Keeping default mux values. Fixed on the i.MX6 QuadPlus
* Need to set the clocks now and make them read-only due to a
* hardware bug. Fixed on the i.MX6 QuadPlus
*/
imx6q_init_ldb_clks(dev);
clk_dm(IMX6QDL_CLK_LDB_DI0_SEL,
imx_clk_mux_flags(dev, "ldb_di0_sel", base + 0x2c, 9, 3,
ldb_di_sels, ARRAY_SIZE(ldb_di_sels),