summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/clk-eyeq.c81
1 files changed, 73 insertions, 8 deletions
diff --git a/drivers/clk/clk-eyeq.c b/drivers/clk/clk-eyeq.c
index ed4dab303d91..85beec6b5b46 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);