/*
* 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.
*
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;
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;
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.
}
}
+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);
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;
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);
}
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;
}
}
+ 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);