diff mbox series

[v2,05/10] clk: eyeq: add fixed factor clocks infrastructure

Message ID 20241106-mbly-clk-v2-5-84cfefb3f485@bootlin.com (mailing list archive)
State New
Headers show
Series Usable clocks on Mobileye EyeQ5 & EyeQ6H | expand

Commit Message

Théo Lebrun Nov. 6, 2024, 4:03 p.m. UTC
Driver can currently host two types of clocks:
 - PLLs derived directly from the main crystal (taken using a fwhandle).
 - Divider clocks derived from those PLLs.

PLLs can be instantiated from of_clk_init() or platform device probe,
using two separate clock providers. Divider clocks are all instantiated
at platform device probe.

Add a third type of clocks: fixed factors. Those can be instantiated at
both stages. They can be parented to any clock from the driver. Early
match data and match data store the list of fixed factor clocks.

Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
---
 drivers/clk/clk-eyeq.c | 81 +++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 73 insertions(+), 8 deletions(-)

Comments

Stephen Boyd Nov. 14, 2024, 11 p.m. UTC | #1
Quoting Théo Lebrun (2024-11-06 08:03:56)
> Driver can currently host two types of clocks:
>  - PLLs derived directly from the main crystal (taken using a fwhandle).
>  - Divider clocks derived from those PLLs.
> 
> PLLs can be instantiated from of_clk_init() or platform device probe,
> using two separate clock providers. Divider clocks are all instantiated
> at platform device probe.
> 
> Add a third type of clocks: fixed factors. Those can be instantiated at
> both stages. They can be parented to any clock from the driver. Early
> match data and match data store the list of fixed factor clocks.
> 
> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
> ---

Applied to clk-next
diff mbox series

Patch

diff --git a/drivers/clk/clk-eyeq.c b/drivers/clk/clk-eyeq.c
index ed4dab303d9121cd8bf453448b4c86547ea9244c..85beec6b5b46f2d3485042bf1aa3e395a29f84b1 100644
--- a/drivers/clk/clk-eyeq.c
+++ b/drivers/clk/clk-eyeq.c
@@ -2,11 +2,14 @@ 
 /*
  * PLL clock driver for the Mobileye EyeQ5, EyeQ6L and EyeQ6H platforms.
  *
- * This controller handles read-only PLLs, all derived from the same main
- * crystal clock. It also exposes divider clocks, those are children to PLLs.
- * Parent clock is expected to be constant. This driver's registers live in
- * a shared region called OLB. Some PLLs are initialised early by of_clk_init();
- * if so, two clk providers are registered.
+ * This controller handles:
+ *  - Read-only PLLs, all derived from the same main crystal clock.
+ *  - It also exposes divider clocks, those are children to PLLs.
+ *  - Fixed factor clocks, children to PLLs.
+ *
+ * Parent clock is expected to be constant. This driver's registers live in a
+ * shared region called OLB. Some PLLs and fixed-factors are initialised early
+ * by of_clk_init(); if so, two clk providers are registered.
  *
  * We use eqc_ as prefix, as-in "EyeQ Clock", but way shorter.
  *
@@ -86,6 +89,14 @@  struct eqc_div {
 	u8		width;
 };
 
+struct eqc_fixed_factor {
+	unsigned int	index;
+	const char	*name;
+	unsigned int	mult;
+	unsigned int	div;
+	unsigned int	parent;
+};
+
 struct eqc_match_data {
 	unsigned int		pll_count;
 	const struct eqc_pll	*plls;
@@ -93,6 +104,9 @@  struct eqc_match_data {
 	unsigned int		div_count;
 	const struct eqc_div	*divs;
 
+	unsigned int			fixed_factor_count;
+	const struct eqc_fixed_factor	*fixed_factors;
+
 	const char		*reset_auxdev_name;
 	const char		*pinctrl_auxdev_name;
 
@@ -103,6 +117,9 @@  struct eqc_early_match_data {
 	unsigned int		early_pll_count;
 	const struct eqc_pll	*early_plls;
 
+	unsigned int			early_fixed_factor_count;
+	const struct eqc_fixed_factor	*early_fixed_factors;
+
 	/*
 	 * We want our of_xlate callback to EPROBE_DEFER instead of dev_err()
 	 * and EINVAL. For that, we must know the total clock count.
@@ -276,6 +293,35 @@  static void eqc_probe_init_divs(struct device *dev, const struct eqc_match_data
 	}
 }
 
+static void eqc_probe_init_fixed_factors(struct device *dev,
+					 const struct eqc_match_data *data,
+					 struct clk_hw_onecell_data *cells)
+{
+	const struct eqc_fixed_factor *ff;
+	struct clk_hw *hw, *parent_hw;
+	unsigned int i;
+
+	for (i = 0; i < data->fixed_factor_count; i++) {
+		ff = &data->fixed_factors[i];
+		parent_hw = cells->hws[ff->parent];
+
+		if (IS_ERR(parent_hw)) {
+			/* Parent is in early clk provider. */
+			hw = clk_hw_register_fixed_factor_index(dev, ff->name,
+					ff->parent, 0, ff->mult, ff->div);
+		} else {
+			/* Avoid clock lookup when we already have the hw reference. */
+			hw = clk_hw_register_fixed_factor_parent_hw(dev, ff->name,
+					parent_hw, 0, ff->mult, ff->div);
+		}
+
+		cells->hws[ff->index] = hw;
+		if (IS_ERR(hw))
+			dev_warn(dev, "failed registering %s: %pe\n",
+				 ff->name, hw);
+	}
+}
+
 static void eqc_auxdev_release(struct device *dev)
 {
 	struct auxiliary_device *adev = to_auxiliary_dev(dev);
@@ -349,10 +395,11 @@  static int eqc_probe(struct platform_device *pdev)
 				 KBUILD_MODNAME, data->pinctrl_auxdev_name, ret);
 	}
 
-	if (data->pll_count + data->div_count == 0)
+	if (data->pll_count + data->div_count + data->fixed_factor_count == 0)
 		return 0; /* Zero clocks, we are done. */
 
-	clk_count = data->pll_count + data->div_count + data->early_clk_count;
+	clk_count = data->pll_count + data->div_count +
+		    data->fixed_factor_count + data->early_clk_count;
 	cells = kzalloc(struct_size(cells, hws, clk_count), GFP_KERNEL);
 	if (!cells)
 		return -ENOMEM;
@@ -367,6 +414,8 @@  static int eqc_probe(struct platform_device *pdev)
 
 	eqc_probe_init_divs(dev, data, base, cells);
 
+	eqc_probe_init_fixed_factors(dev, data, cells);
+
 	return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells);
 }
 
@@ -580,7 +629,8 @@  static void __init eqc_early_init(struct device_node *np,
 	void __iomem *base;
 	int ret;
 
-	clk_count = early_data->early_pll_count + early_data->late_clk_count;
+	clk_count = early_data->early_pll_count + early_data->early_fixed_factor_count +
+		    early_data->late_clk_count;
 	cells = kzalloc(struct_size(cells, hws, clk_count), GFP_KERNEL);
 	if (!cells) {
 		ret = -ENOMEM;
@@ -633,6 +683,21 @@  static void __init eqc_early_init(struct device_node *np,
 		}
 	}
 
+	for (i = 0; i < early_data->early_fixed_factor_count; i++) {
+		const struct eqc_fixed_factor *ff = &early_data->early_fixed_factors[i];
+		struct clk_hw *parent_hw = cells->hws[ff->parent];
+		struct clk_hw *hw;
+
+		hw = clk_hw_register_fixed_factor_parent_hw(NULL, ff->name,
+				parent_hw, 0, ff->mult, ff->div);
+		cells->hws[ff->index] = hw;
+		if (IS_ERR(hw)) {
+			pr_err("failed registering %s: %pe\n", ff->name, hw);
+			ret = PTR_ERR(hw);
+			goto err;
+		}
+	}
+
 	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells);
 	if (ret) {
 		pr_err("failed registering clk provider: %d\n", ret);