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(-)
diff --git a/drivers/clk/clk-eyeq.c b/drivers/clk/clk-eyeq.c
index ed4dab303d9121cd8bf453448b4c86547ea9244c..dcd1d996255fc97449ac1bccb6a7c810d6e4c9db 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);
--
2.47.0